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.IntraCoreApi
51 public class NativeAllocationRegistry {
52 
53     private final ClassLoader classLoader;
54 
55     // Pointer to native deallocation function of type void f(void* freeFunction).
56     private final long freeFunction;
57 
58     // The size of the registered native objects. This can be, and usually is, approximate.
59     // The least significant bit is one iff the object was allocated primarily with system
60     // malloc().
61     // This field is examined by ahat and other tools. We chose this encoding of the "is_malloced"
62     // information to (a) allow existing readers to continue to work with minimal confusion,
63     // and (b) to avoid adding a field to NativeAllocationRegistry objects.
64     private final long size;
65     // Bit mask for "is_malloced" information.
66     private static final long IS_MALLOCED = 0x1;
67     // Assumed size for malloced objects that don't specify a size.
68     // We use an even value close to 100 that is unlikely to be explicitly provided.
69     private static final long DEFAULT_SIZE = 98;
70 
71     /**
72      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
73      * allocated by means other than the system memory allocator. For example,
74      * the memory may be allocated directly with mmap.
75      * @param classLoader  ClassLoader that was used to load the native
76      *                     library defining freeFunction.
77      *                     This ensures that the the native library isn't unloaded
78      *                     before {@code freeFunction} is called.
79      * @param freeFunction address of a native function of type
80      *                     {@code void f(void* nativePtr)} used to free this
81      *                     kind of native allocation
82      * @param size         estimated size in bytes of the part of the described
83      *                     native memory that is not allocated with system malloc.
84      *                     Used as input to the garbage collector triggering algorithm,
85      *                     and by heap analysis tools.
86      *                     Approximate values are acceptable.
87      * @return allocated {@link NativeAllocationRegistry}
88      * @throws IllegalArgumentException If {@code size} is negative
89      *
90      * @hide
91      */
92     @SystemApi(client = MODULE_LIBRARIES)
createNonmalloced( @onNull ClassLoader classLoader, long freeFunction, long size)93     public static NativeAllocationRegistry createNonmalloced(
94             @NonNull ClassLoader classLoader, long freeFunction, long size) {
95         return new NativeAllocationRegistry(classLoader, freeFunction, size, false);
96     }
97 
98     /**
99      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
100      * allocated by the system memory allocator.
101      * For example, the memory may be allocated directly with new or malloc.
102      * <p>
103      * The native function should have the type:
104      * <pre>
105      *    void f(void* nativePtr);
106      * </pre>
107      * <p>
108      * @param classLoader  ClassLoader that was used to load the native
109      *                     library {@code freeFunction} belongs to.
110      * @param freeFunction address of a native function of type
111      *                     {@code void f(void* nativePtr)} used to free this
112      *                     kind of native allocation
113      * @param size         estimated size in bytes of the part of the described
114      *                     native memory allocated with system malloc.
115      *                     Approximate values, including wild guesses, are acceptable.
116      *                     Unlike {@code createNonmalloced()}, this size is used
117      *                     only by heap analysis tools; garbage collector triggering
118      *                     instead looks directly at {@code mallinfo()} information.
119      * @return allocated {@link NativeAllocationRegistry}
120      * @throws IllegalArgumentException If {@code size} is negative
121      *
122      * @hide
123      */
124     @SystemApi(client = MODULE_LIBRARIES)
createMalloced( @onNull ClassLoader classLoader, long freeFunction, long size)125     public static NativeAllocationRegistry createMalloced(
126             @NonNull ClassLoader classLoader, long freeFunction, long size) {
127         return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
128     }
129 
130     /**
131      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
132      * allocated by the system memory allocator. This version uses a default size,
133      * thus providing less information than desired for heap analysis tools.
134      * It should only be used when the native allocation is expected to be small,
135      * but there is no reasonable way to provide a meaningful size estimate.
136      * @param classLoader  ClassLoader that was used to load the native
137      *                     library {@code freeFunction} belongs to.
138      * @param freeFunction address of a native function of type
139      *                     {@code void f(void* nativePtr)} used to free this
140      *                     kind of native allocation
141      * @return allocated {@link NativeAllocationRegistry}
142      *
143      * @hide
144      */
145     @SystemApi(client = MODULE_LIBRARIES)
146     @libcore.api.IntraCoreApi
createMalloced( @onNull ClassLoader classLoader, long freeFunction)147     public static NativeAllocationRegistry createMalloced(
148             @NonNull ClassLoader classLoader, long freeFunction) {
149         return new NativeAllocationRegistry(classLoader, freeFunction, DEFAULT_SIZE, true);
150     }
151 
152     /**
153      * Constructs a NativeAllocationRegistry for a particular kind of native
154      * allocation.
155      * <p>
156      * The <code>size</code> should be an estimate of the total number of
157      * native bytes this kind of native allocation takes up. Different
158      * NativeAllocationRegistrys must be used to register native allocations
159      * with different estimated sizes, even if they use the same
160      * <code>freeFunction</code>. This is used to help inform the garbage
161      * collector about the possible need for collection. Memory allocated with
162      * native malloc is implicitly included, and ideally should not be included in this
163      * argument.
164      * <p>
165      * @param classLoader  ClassLoader that was used to load the native
166      *                     library freeFunction belongs to.
167      * @param freeFunction address of a native function used to free this
168      *                     kind of native allocation
169      * @param size         estimated size in bytes of this kind of native
170      *                     allocation. If mallocAllocation is false, then this
171      *                     should ideally exclude memory allocated by system
172      *                     malloc. However including it will simply double-count it,
173      *                     typically resulting in slightly increased GC frequency.
174      *                     If mallocAllocation is true, then this affects only the
175      *                     frequency with which we sample the malloc heap, and debugging
176      *                     tools. In this case a value of zero is commonly used to
177      *                     indicate an unknown non-huge size.
178      * @param mallocAllocation the native object is primarily allocated via malloc.
179      */
NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size, boolean mallocAllocation)180     private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size,
181             boolean mallocAllocation) {
182         if (size < 0) {
183             throw new IllegalArgumentException("Invalid native allocation size: " + size);
184         }
185         this.classLoader = classLoader;
186         this.freeFunction = freeFunction;
187         this.size = mallocAllocation ? (size | IS_MALLOCED) : (size & ~IS_MALLOCED);
188     }
189 
190     /**
191      * Constructs a {@link NativeAllocationRegistry} for a particular kind of native
192      * allocation.
193      * <p>
194      * New code should use the preceding factory methods rather than calling this
195      * constructor directly.
196      * <p>
197      * The {@code size} should be an estimate of the total number of
198      * native bytes this kind of native allocation takes up excluding bytes allocated
199      * with system malloc. Different
200      * {@link NativeAllocationRegistry}s must be used to register native allocations
201      * with different estimated sizes, even if they use the same
202      * {@code freeFunction}. This is used to help inform the garbage
203      * collector about the possible need for collection. Memory allocated with
204      * native malloc is implicitly included, and ideally should not be included in this
205      * argument.
206      * <p>
207      * @param classLoader  ClassLoader that was used to load the native
208      *                     library {@code freeFunction} belongs to.
209      * @param freeFunction address of a native function used to free this
210      *                     kind of native allocation
211      * @param size         estimated size in bytes of this kind of native
212      *                     allocation, excluding memory allocated with system malloc.
213      *                     A value of 0 indicates that the memory was allocated mainly
214      *                     with malloc.
215      *
216      * @hide
217      */
218     @SystemApi(client = MODULE_LIBRARIES)
NativeAllocationRegistry(@onNull ClassLoader classLoader, long freeFunction, long size)219     public NativeAllocationRegistry(@NonNull ClassLoader classLoader, long freeFunction, long size) {
220         this(classLoader, freeFunction, size, size == 0);
221     }
222 
223     /**
224      * Registers a new native allocation and associated Java object with the
225      * runtime.
226      * This {@link NativeAllocationRegistry}'s {@code freeFunction} will
227      * automatically be called with {@code nativePtr} as its sole
228      * argument when {@code referent} becomes unreachable. If you
229      * maintain copies of {@code nativePtr} outside
230      * {@code referent}, you must not access these after
231      * {@code referent} becomes unreachable, because they may be dangling
232      * pointers.
233      * <p>
234      * The returned Runnable can be used to free the native allocation before
235      * {@code referent} becomes unreachable. The runnable will have no
236      * effect if the native allocation has already been freed by the runtime
237      * or by using the runnable.
238      * <p>
239      * WARNING: This unconditionally takes ownership, i.e. deallocation
240      * responsibility of nativePtr. nativePtr will be DEALLOCATED IMMEDIATELY
241      * if the registration attempt throws an exception (other than one reporting
242      * a programming error).
243      *
244      * @param referent      Non-{@code null} java object to associate the native allocation with
245      * @param nativePtr     Non-zero address of the native allocation
246      * @return runnable to explicitly free native allocation
247      * @throws IllegalArgumentException if either referent or nativePtr is {@code null}.
248      * @throws OutOfMemoryError  if there is not enough space on the Java heap
249      *                           in which to register the allocation. In this
250      *                           case, {@code freeFunction} will be
251      *                           called with {@code nativePtr} as its
252      *                           argument before the {@link OutOfMemoryError} is
253      *                           thrown.
254      *
255      * @hide
256      */
257     @SystemApi(client = MODULE_LIBRARIES)
258     @libcore.api.IntraCoreApi
registerNativeAllocation(@onNull Object referent, long nativePtr)259     public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
260         if (referent == null) {
261             throw new IllegalArgumentException("referent is null");
262         }
263         if (nativePtr == 0) {
264             throw new IllegalArgumentException("nativePtr is null");
265         }
266 
267         CleanerThunk thunk;
268         CleanerRunner result;
269         try {
270             thunk = new CleanerThunk();
271             Cleaner cleaner = Cleaner.create(referent, thunk);
272             result = new CleanerRunner(cleaner);
273             registerNativeAllocation(this.size);
274         } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
275             applyFreeFunction(freeFunction, nativePtr);
276             throw vme;
277         } // Other exceptions are impossible.
278         // Enable the cleaner only after we can no longer throw anything, including OOME.
279         thunk.setNativePtr(nativePtr);
280         // Ensure that cleaner doesn't get invoked before we enable it.
281         Reference.reachabilityFence(referent);
282         return result;
283     }
284 
285     private class CleanerThunk implements Runnable {
286         private long nativePtr;
287 
CleanerThunk()288         public CleanerThunk() {
289             this.nativePtr = 0;
290         }
291 
run()292         public void run() {
293             if (nativePtr != 0) {
294                 applyFreeFunction(freeFunction, nativePtr);
295                 registerNativeFree(size);
296             }
297         }
298 
setNativePtr(long nativePtr)299         public void setNativePtr(long nativePtr) {
300             this.nativePtr = nativePtr;
301         }
302 
303         // Only for error reporting.
toString()304         @Override public String toString() {
305             return super.toString() + "(freeFunction = 0x" + Long.toHexString(freeFunction)
306                 + ", nativePtr = 0x" + Long.toHexString(nativePtr) + ", size = " + size + ")";
307         }
308     }
309 
310     private static class CleanerRunner implements Runnable {
311         private final Cleaner cleaner;
312 
CleanerRunner(Cleaner cleaner)313         public CleanerRunner(Cleaner cleaner) {
314             this.cleaner = cleaner;
315         }
316 
run()317         public void run() {
318             cleaner.clean();
319         }
320     }
321 
322     // Inform the garbage collector of the allocation. We do this differently for
323     // malloc-based allocations.
registerNativeAllocation(long size)324     private static void registerNativeAllocation(long size) {
325         VMRuntime runtime = VMRuntime.getRuntime();
326         if ((size & IS_MALLOCED) != 0) {
327             final long notifyImmediateThreshold = 300000;
328             if (size >= notifyImmediateThreshold) {
329                 runtime.notifyNativeAllocationsInternal();
330             } else {
331                 runtime.notifyNativeAllocation();
332             }
333         } else {
334             runtime.registerNativeAllocation(size);
335         }
336     }
337 
338     // Inform the garbage collector of deallocation, if appropriate.
registerNativeFree(long size)339     private static void registerNativeFree(long size) {
340         if ((size & IS_MALLOCED) == 0) {
341             VMRuntime.getRuntime().registerNativeFree(size);
342         }
343     }
344 
345     /**
346      * Calls {@code freeFunction}({@code nativePtr}).
347      * Provided as a convenience in the case where you wish to manually free a
348      * native allocation using a {@code freeFunction} without using a
349      * {@link NativeAllocationRegistry}.
350      *
351      * @param freeFunction address of a native function used to free this
352      *                     kind of native allocation
353      * @param nativePtr    pointer to pass to freeing function
354      *
355      * @hide
356      */
357     @SystemApi(client = MODULE_LIBRARIES)
applyFreeFunction(long freeFunction, long nativePtr)358     public static native void applyFreeFunction(long freeFunction, long nativePtr);
359 }
360 
361