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