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