1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package libcore.util;
18 
19 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
20 
21 import android.annotation.SystemApi;
22 
23 import dalvik.system.VMRuntime;
24 import sun.misc.Cleaner;
25 
26 import java.lang.ref.Reference;
27 
28 import libcore.util.NonNull;
29 
30 /**
31  * A NativeAllocationRegistry is used to associate native allocations with
32  * Java objects and register them with the runtime.
33  * There are two primary benefits of registering native allocations associated
34  * with Java objects:
35  * <ol>
36  *  <li>The runtime will account for the native allocations when scheduling
37  *  garbage collection to run.</li>
38  *  <li>The runtime will arrange for the native allocation to be automatically
39  *  freed by a user-supplied function when the associated Java object becomes
40  *  unreachable.</li>
41  * </ol>
42  * A separate NativeAllocationRegistry should be instantiated for each kind
43  * of native allocation, where the kind of a native allocation consists of the
44  * native function used to free the allocation and the estimated size of the
45  * allocation. Once a NativeAllocationRegistry is instantiated, it can be
46  * used to register any number of native allocations of that kind.
47  * @hide
48  */
49 @SystemApi(client = MODULE_LIBRARIES)
50 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
51 @libcore.api.IntraCoreApi
52 public class NativeAllocationRegistry {
53 
54     private final ClassLoader classLoader;
55 
56     // Pointer to native deallocation function of type void f(void* freeFunction).
57     private final long freeFunction;
58 
59     // The size of the registered native objects. This can be, and usually is, approximate.
60     // The least significant bit is one iff the object was allocated primarily with system
61     // malloc().
62     // This field is examined by ahat and other tools. We chose this encoding of the "is_malloced"
63     // information to (a) allow existing readers to continue to work with minimal confusion,
64     // and (b) to avoid adding a field to NativeAllocationRegistry objects.
65     private final long size;
66     // Bit mask for "is_malloced" information.
67     private static final long IS_MALLOCED = 0x1;
68 
69     /**
70      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
71      * allocated by means other than the system memory allocator. For example,
72      * the memory may be allocated directly with mmap.
73      * @param classLoader  ClassLoader that was used to load the native
74      *                     library defining freeFunction.
75      *                     This ensures that the the native library isn't unloaded
76      *                     before {@code freeFunction} is called.
77      * @param freeFunction address of a native function of type
78      *                     {@code void f(void* nativePtr)} used to free this
79      *                     kind of native allocation
80      * @param size         estimated size in bytes of the part of the described
81      *                     native memory that is not allocated with system malloc.
82      *                     Approximate values are acceptable.
83      * @return allocated {@link NativeAllocationRegistry}
84      * @throws IllegalArgumentException If {@code size} is negative
85      *
86      * @hide
87      */
88     @SystemApi(client = MODULE_LIBRARIES)
89     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
createNonmalloced( @onNull ClassLoader classLoader, long freeFunction, long size)90     public static NativeAllocationRegistry createNonmalloced(
91             @NonNull ClassLoader classLoader, long freeFunction, long size) {
92         return new NativeAllocationRegistry(classLoader, freeFunction, size, false);
93     }
94 
95     /**
96      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
97      * allocated by the system memory allocator.
98      * For example, the memory may be allocated directly with new or malloc.
99      * <p>
100      * The native function should have the type:
101      * <pre>
102      *    void f(void* nativePtr);
103      * </pre>
104      * <p>
105      * @param classLoader  ClassLoader that was used to load the native
106      *                     library {@code freeFunction} belongs to.
107      * @param freeFunction address of a native function of type
108      *                     {@code void f(void* nativePtr)} used to free this
109      *                     kind of native allocation
110      * @param size         estimated size in bytes of the part of the described
111      *                     native memory allocated with system malloc.
112      *                     Approximate values are acceptable. For sizes less than
113      *                     a few hundered KB, use the simplified overload below.
114      * @return allocated {@link NativeAllocationRegistry}
115      * @throws IllegalArgumentException If {@code size} is negative
116      *
117      * @hide
118      */
119     @SystemApi(client = MODULE_LIBRARIES)
120     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
createMalloced( @onNull ClassLoader classLoader, long freeFunction, long size)121     public static NativeAllocationRegistry createMalloced(
122             @NonNull ClassLoader classLoader, long freeFunction, long size) {
123         return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
124     }
125 
126     /**
127      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
128      * allocated by the system memory allocator. This version is preferred
129      * for smaller objects (typically less than a few hundred KB).
130      * @param classLoader  ClassLoader that was used to load the native
131      *                     library {@code freeFunction} belongs to.
132      * @param freeFunction address of a native function of type
133      *                     {@code void f(void* nativePtr)} used to free this
134      *                     kind of native allocation
135      * @return allocated {@link NativeAllocationRegistry}
136      *
137      * @hide
138      */
139     @SystemApi(client = MODULE_LIBRARIES)
140     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
141     @libcore.api.IntraCoreApi
createMalloced( @onNull ClassLoader classLoader, long freeFunction)142     public static NativeAllocationRegistry createMalloced(
143             @NonNull ClassLoader classLoader, long freeFunction) {
144         return new NativeAllocationRegistry(classLoader, freeFunction, 0, true);
145     }
146 
147     /**
148      * Constructs a NativeAllocationRegistry for a particular kind of native
149      * allocation.
150      * <p>
151      * The <code>size</code> should be an estimate of the total number of
152      * native bytes this kind of native allocation takes up. Different
153      * NativeAllocationRegistrys must be used to register native allocations
154      * with different estimated sizes, even if they use the same
155      * <code>freeFunction</code>. This is used to help inform the garbage
156      * collector about the possible need for collection. Memory allocated with
157      * native malloc is implicitly included, and ideally should not be included in this
158      * argument.
159      * <p>
160      * @param classLoader  ClassLoader that was used to load the native
161      *                     library freeFunction belongs to.
162      * @param freeFunction address of a native function used to free this
163      *                     kind of native allocation
164      * @param size         estimated size in bytes of this kind of native
165      *                     allocation. If mallocAllocation is false, then this
166      *                     should ideally exclude memory allocated by system
167      *                     malloc. However including it will simply double-count it,
168      *                     typically resulting in slightly increased GC frequency.
169      *                     If mallocAllocation is true, then this affects only the
170      *                     frequency with which we sample the malloc heap, and debugging
171      *                     tools. In this case a value of zero is commonly used to
172      *                     indicate an unknown non-huge size.
173      * @param mallocAllocation the native object is primarily allocated via malloc.
174      */
NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size, boolean mallocAllocation)175     private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size,
176             boolean mallocAllocation) {
177         if (size < 0) {
178             throw new IllegalArgumentException("Invalid native allocation size: " + size);
179         }
180         this.classLoader = classLoader;
181         this.freeFunction = freeFunction;
182         this.size = mallocAllocation ? (size | IS_MALLOCED) : (size & ~IS_MALLOCED);
183     }
184 
185     /**
186      * Constructs a {@link NativeAllocationRegistry} for a particular kind of native
187      * allocation.
188      * <p>
189      * New code should use the preceding factory methods rather than calling this
190      * constructor directly.
191      * <p>
192      * The {@code size} should be an estimate of the total number of
193      * native bytes this kind of native allocation takes up excluding bytes allocated
194      * with system malloc. Different
195      * {@link NativeAllocationRegistry}s must be used to register native allocations
196      * with different estimated sizes, even if they use the same
197      * {@code freeFunction}. This is used to help inform the garbage
198      * collector about the possible need for collection. Memory allocated with
199      * native malloc is implicitly included, and ideally should not be included in this
200      * argument.
201      * <p>
202      * @param classLoader  ClassLoader that was used to load the native
203      *                     library {@code freeFunction} belongs to.
204      * @param freeFunction address of a native function used to free this
205      *                     kind of native allocation
206      * @param size         estimated size in bytes of this kind of native
207      *                     allocation, excluding memory allocated with system malloc.
208      *                     A value of 0 indicates that the memory was allocated mainly
209      *                     with malloc.
210      *
211      * @hide
212      */
213     @SystemApi(client = MODULE_LIBRARIES)
214     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
NativeAllocationRegistry(@onNull ClassLoader classLoader, long freeFunction, long size)215     public NativeAllocationRegistry(@NonNull ClassLoader classLoader, long freeFunction, long size) {
216         this(classLoader, freeFunction, size, size == 0);
217     }
218 
219     /**
220      * Registers a new native allocation and associated Java object with the
221      * runtime.
222      * This {@link NativeAllocationRegistry}'s {@code freeFunction} will
223      * automatically be called with {@code nativePtr} as its sole
224      * argument when {@code referent} becomes unreachable. If you
225      * maintain copies of {@code nativePtr} outside
226      * {@code referent}, you must not access these after
227      * {@code referent} becomes unreachable, because they may be dangling
228      * pointers.
229      * <p>
230      * The returned Runnable can be used to free the native allocation before
231      * {@code referent} becomes unreachable. The runnable will have no
232      * effect if the native allocation has already been freed by the runtime
233      * or by using the runnable.
234      * <p>
235      * WARNING: This unconditionally takes ownership, i.e. deallocation
236      * responsibility of nativePtr. nativePtr will be DEALLOCATED IMMEDIATELY
237      * if the registration attempt throws an exception (other than one reporting
238      * a programming error).
239      *
240      * @param referent      Non-{@code null} java object to associate the native allocation with
241      * @param nativePtr     Non-zero address of the native allocation
242      * @return runnable to explicitly free native allocation
243      * @throws IllegalArgumentException if either referent or nativePtr is {@code null}.
244      * @throws OutOfMemoryError  if there is not enough space on the Java heap
245      *                           in which to register the allocation. In this
246      *                           case, {@code freeFunction} will be
247      *                           called with {@code nativePtr} as its
248      *                           argument before the {@link OutOfMemoryError} is
249      *                           thrown.
250      *
251      * @hide
252      */
253     @SystemApi(client = MODULE_LIBRARIES)
254     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
255     @libcore.api.IntraCoreApi
registerNativeAllocation(@onNull Object referent, long nativePtr)256     public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
257         if (referent == null) {
258             throw new IllegalArgumentException("referent is null");
259         }
260         if (nativePtr == 0) {
261             throw new IllegalArgumentException("nativePtr is null");
262         }
263 
264         CleanerThunk thunk;
265         CleanerRunner result;
266         try {
267             thunk = new CleanerThunk();
268             Cleaner cleaner = Cleaner.create(referent, thunk);
269             result = new CleanerRunner(cleaner);
270             registerNativeAllocation(this.size);
271         } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
272             applyFreeFunction(freeFunction, nativePtr);
273             throw vme;
274         } // Other exceptions are impossible.
275         // Enable the cleaner only after we can no longer throw anything, including OOME.
276         thunk.setNativePtr(nativePtr);
277         // Ensure that cleaner doesn't get invoked before we enable it.
278         Reference.reachabilityFence(referent);
279         return result;
280     }
281 
282     private class CleanerThunk implements Runnable {
283         private long nativePtr;
284 
CleanerThunk()285         public CleanerThunk() {
286             this.nativePtr = 0;
287         }
288 
run()289         public void run() {
290             if (nativePtr != 0) {
291                 applyFreeFunction(freeFunction, nativePtr);
292                 registerNativeFree(size);
293             }
294         }
295 
setNativePtr(long nativePtr)296         public void setNativePtr(long nativePtr) {
297             this.nativePtr = nativePtr;
298         }
299     }
300 
301     private static class CleanerRunner implements Runnable {
302         private final Cleaner cleaner;
303 
CleanerRunner(Cleaner cleaner)304         public CleanerRunner(Cleaner cleaner) {
305             this.cleaner = cleaner;
306         }
307 
run()308         public void run() {
309             cleaner.clean();
310         }
311     }
312 
313     // Inform the garbage collector of the allocation. We do this differently for
314     // malloc-based allocations.
registerNativeAllocation(long size)315     private static void registerNativeAllocation(long size) {
316         VMRuntime runtime = VMRuntime.getRuntime();
317         if ((size & IS_MALLOCED) != 0) {
318             final long notifyImmediateThreshold = 300000;
319             if (size >= notifyImmediateThreshold) {
320                 runtime.notifyNativeAllocationsInternal();
321             } else {
322                 runtime.notifyNativeAllocation();
323             }
324         } else {
325             runtime.registerNativeAllocation(size);
326         }
327     }
328 
329     // Inform the garbage collector of deallocation, if appropriate.
registerNativeFree(long size)330     private static void registerNativeFree(long size) {
331         if ((size & IS_MALLOCED) == 0) {
332             VMRuntime.getRuntime().registerNativeFree(size);
333         }
334     }
335 
336     /**
337      * Calls {@code freeFunction}({@code nativePtr}).
338      * Provided as a convenience in the case where you wish to manually free a
339      * native allocation using a {@code freeFunction} without using a
340      * {@link NativeAllocationRegistry}.
341      *
342      * @param freeFunction address of a native function used to free this
343      *                     kind of native allocation
344      * @param nativePtr    pointer to pass to freeing function
345      *
346      * @hide
347      */
348     @SystemApi(client = MODULE_LIBRARIES)
349     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
applyFreeFunction(long freeFunction, long nativePtr)350     public static native void applyFreeFunction(long freeFunction, long nativePtr);
351 }
352 
353