• 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 import android.annotation.FlaggedApi;
23 
24 import dalvik.system.VMRuntime;
25 import sun.misc.Cleaner;
26 
27 import java.lang.invoke.MethodHandles;
28 import java.lang.invoke.VarHandle;
29 import java.lang.ref.Reference;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.WeakHashMap;
36 
37 import libcore.util.NonNull;
38 
39 /**
40  * A NativeAllocationRegistry is used to associate native allocations with
41  * Java objects and register them with the runtime.
42  * There are two primary benefits of registering native allocations associated
43  * with Java objects:
44  * <ol>
45  *  <li>The runtime will account for the native allocations when scheduling
46  *  garbage collection to run.</li>
47  *  <li>The runtime will arrange for the native allocation to be automatically
48  *  freed by a user-supplied function when the associated Java object becomes
49  *  unreachable.</li>
50  * </ol>
51  * A separate NativeAllocationRegistry should be instantiated for each kind
52  * of native allocation, where the kind of a native allocation consists of the
53  * native function used to free the allocation and the estimated size of the
54  * allocation. Once a NativeAllocationRegistry is instantiated, it can be
55  * used to register any number of native allocations of that kind.
56  *
57  * @hide
58  */
59 @SystemApi(client = MODULE_LIBRARIES)
60 @libcore.api.IntraCoreApi
61 public class NativeAllocationRegistry {
62 
63     // Class associated with this NativeAllocationRegistry. If no class is explicitly
64     // specified, NativeAllocationRegistry.class will be used as default
65     private final Class clazz;
66 
67     // ClassLoader for holding the freeFunction in place. If no ClassLoader is
68     // explicitly specified, it will be inferred from `clazz.getClassLoader()`.
69     private final ClassLoader classLoader;
70 
71     // Pointer to native deallocation function of type void f(void* freeFunction).
72     private final long freeFunction;
73 
74     // The size of the registered native objects. This can be, and usually is, approximate.
75     // The least significant bit is one iff the object was allocated primarily with system
76     // malloc().
77     // This field is examined by ahat and other tools. We chose this encoding of the "is_malloced"
78     // information to (a) allow existing readers to continue to work with minimal confusion,
79     // and (b) to avoid adding a field to NativeAllocationRegistry objects.
80     private final long size;
81     // Bit mask for "is_malloced" information.
82     private static final long IS_MALLOCED = 0x1;
83     // Assumed size for malloced objects that don't specify a size.
84     // We use an even value close to 100 that is unlikely to be explicitly provided.
85     private static final long DEFAULT_SIZE = 98;
86 
isMalloced()87     private boolean isMalloced() {
88         return (size & IS_MALLOCED) == IS_MALLOCED;
89     }
90 
91     // This is ONLY used to gather statistics.  A WeakHashMap is used here to track
92     // all registries created without holding strong references, therefore allow the
93     // unreferenced registries to be GC'ed.
94     private static final Map<NativeAllocationRegistry, Void> registries = new WeakHashMap<>();
95 
96     /**
97      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
98      * allocated by means other than the system memory allocator. For example,
99      * the memory may be allocated directly with mmap.
100      * @param classLoader  ClassLoader that was used to load the native
101      *                     library defining freeFunction.
102      *                     This ensures that the native library isn't unloaded
103      *                     before {@code freeFunction} is called.
104      * @param freeFunction address of a native function of type
105      *                     {@code void f(void* nativePtr)} used to free this
106      *                     kind of native allocation
107      * @param size         estimated size in bytes of the part of the described
108      *                     native memory that is not allocated with system malloc.
109      *                     Used as input to the garbage collector triggering algorithm,
110      *                     and by heap analysis tools.
111      *                     Approximate values are acceptable.
112      * @return allocated {@link NativeAllocationRegistry}
113      * @throws IllegalArgumentException If {@code size} is negative
114      *
115      * @hide
116      */
117     @SystemApi(client = MODULE_LIBRARIES)
createNonmalloced( @onNull ClassLoader classLoader, long freeFunction, long size)118     public static @NonNull NativeAllocationRegistry createNonmalloced(
119             @NonNull ClassLoader classLoader, long freeFunction, long size) {
120         return new NativeAllocationRegistry(
121             classLoader, NativeAllocationRegistry.class, freeFunction, size, false);
122     }
123 
124     /**
125      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
126      * allocated by means other than the system memory allocator. This version requires
127      * a Class to be specified and its ClassLoader is implied.
128      * @param clazz        Class that is associated with the native memory allocation.
129      *                     This allows per-class metrics to be maintained.
130      *                     The ClassLoader will be obtained from this Class.
131      *                     This ensures that the native library isn't unloaded
132      *                     before {@code freeFunction} is called.
133      * @param freeFunction address of a native function of type
134      *                     {@code void f(void* nativePtr)} used to free this
135      *                     kind of native allocation
136      * @param size         estimated size in bytes of the part of the described
137      *                     native memory that is not allocated with system malloc.
138      *                     Used as input to the garbage collector triggering algorithm,
139      *                     and by heap analysis tools.
140      *                     Approximate values are acceptable.
141      * @return allocated {@link NativeAllocationRegistry}
142      * @throws IllegalArgumentException If {@code size} is negative
143      *
144      * @hide
145      */
146     @SystemApi(client = MODULE_LIBRARIES)
147     @FlaggedApi(com.android.libcore.Flags.FLAG_NATIVE_METRICS)
createNonmalloced( @onNull Class clazz, long freeFunction, long size)148     public static @NonNull NativeAllocationRegistry createNonmalloced(
149             @NonNull Class clazz, long freeFunction, long size) {
150         return new NativeAllocationRegistry(
151             clazz.getClassLoader(), clazz, freeFunction, size, false);
152     }
153 
154     /**
155      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
156      * allocated by the system memory allocator.
157      * For example, the memory may be allocated directly with new or malloc.
158      * <p>
159      * The native function should have the type:
160      * <pre>
161      *    void f(void* nativePtr);
162      * </pre>
163      * <p>
164      * @param classLoader  ClassLoader that was used to load the native
165      *                     library {@code freeFunction} belongs to.
166      * @param freeFunction address of a native function of type
167      *                     {@code void f(void* nativePtr)} used to free this
168      *                     kind of native allocation
169      * @param size         estimated size in bytes of the part of the described
170      *                     native memory allocated with system malloc.
171      *                     Approximate values, including wild guesses, are acceptable.
172      *                     Unlike {@code createNonmalloced()}, this size is used
173      *                     only by heap analysis tools; garbage collector triggering
174      *                     instead looks directly at {@code mallinfo()} information.
175      * @return allocated {@link NativeAllocationRegistry}
176      * @throws IllegalArgumentException If {@code size} is negative
177      *
178      * @hide
179      */
180     @SystemApi(client = MODULE_LIBRARIES)
createMalloced( @onNull ClassLoader classLoader, long freeFunction, long size)181     public static @NonNull NativeAllocationRegistry createMalloced(
182             @NonNull ClassLoader classLoader, long freeFunction, long size) {
183         return new NativeAllocationRegistry(
184             classLoader, NativeAllocationRegistry.class, freeFunction, size, true);
185     }
186 
187     /**
188      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
189      * allocated by the system memory allocator. This version uses a default size,
190      * thus providing less information than desired for heap analysis tools.
191      * It should only be used when the native allocation is expected to be small,
192      * but there is no reasonable way to provide a meaningful size estimate.
193      * @param classLoader  ClassLoader that was used to load the native
194      *                     library {@code freeFunction} belongs to.
195      * @param freeFunction address of a native function of type
196      *                     {@code void f(void* nativePtr)} used to free this
197      *                     kind of native allocation
198      * @return allocated {@link NativeAllocationRegistry}
199      *
200      * @hide
201      */
202     @SystemApi(client = MODULE_LIBRARIES)
203     @libcore.api.IntraCoreApi
createMalloced( @onNull ClassLoader classLoader, long freeFunction)204     public static @NonNull NativeAllocationRegistry createMalloced(
205             @NonNull ClassLoader classLoader, long freeFunction) {
206         return new NativeAllocationRegistry(
207             classLoader, NativeAllocationRegistry.class, freeFunction, DEFAULT_SIZE, true);
208     }
209 
210     /**
211      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
212      * allocated by the system memory allocator.  This version requires a Class to
213      * be specified and its ClassLoader is implied.
214      * @param clazz        Class that is associated with the native memory allocation.
215      *                     This allows per-class metrics to be maintained.
216      *                     The ClassLoader will be obtained from this Class.
217      *                     This ensures that the native library isn't unloaded
218      *                     before {@code freeFunction} is called.
219      * @param freeFunction address of a native function of type
220      *                     {@code void f(void* nativePtr)} used to free this
221      *                     kind of native allocation
222      * @param size         estimated size in bytes of the part of the described
223      *                     native memory allocated with system malloc.
224      *                     Approximate values, including wild guesses, are acceptable.
225      *                     Unlike {@code createNonmalloced()}, this size is used
226      *                     only by heap analysis tools; garbage collector triggering
227      *                     instead looks directly at {@code mallinfo()} information.
228      * @return allocated {@link NativeAllocationRegistry}
229      * @throws IllegalArgumentException If {@code size} is negative
230      *
231      * @hide
232      */
233     @SystemApi(client = MODULE_LIBRARIES)
234     @FlaggedApi(com.android.libcore.Flags.FLAG_NATIVE_METRICS)
createMalloced( @onNull Class clazz, long freeFunction, long size)235     public static @NonNull NativeAllocationRegistry createMalloced(
236             @NonNull Class clazz, long freeFunction, long size) {
237         return new NativeAllocationRegistry(
238             clazz.getClassLoader(), clazz, freeFunction, size, true);
239     }
240 
241     /**
242      * Return a {@link NativeAllocationRegistry} for native memory that is mostly
243      * allocated by the system memory allocator.  This version uses a default size,
244      * thus providing less information than desired for heap analysis tools.
245      * It should only be used when the native allocation is expected to be small,
246      * but there is no reasonable way to provide a meaningful size estimate.
247      * @param clazz        Class that is associated with the native memory allocation.
248      *                     This allows per-class metrics to be maintained.
249      *                     The ClassLoader will be obtained from this Class.
250      *                     This ensures that the native library isn't unloaded
251      *                     before {@code freeFunction} is called.
252      * @param freeFunction address of a native function of type
253      *                     {@code void f(void* nativePtr)} used to free this
254      *                     kind of native allocation
255      * @return allocated {@link NativeAllocationRegistry}
256      *
257      * @hide
258      */
259     @SystemApi(client = MODULE_LIBRARIES)
260     @FlaggedApi(com.android.libcore.Flags.FLAG_NATIVE_METRICS)
createMalloced( @onNull Class clazz, long freeFunction)261     public static @NonNull NativeAllocationRegistry createMalloced(
262             @NonNull Class clazz, long freeFunction) {
263         return new NativeAllocationRegistry(
264             clazz.getClassLoader(), clazz, freeFunction, DEFAULT_SIZE, true);
265     }
266 
267     /**
268      * Constructs a NativeAllocationRegistry for a particular kind of native
269      * allocation.
270      * <p>
271      * The <code>size</code> should be an estimate of the total number of
272      * native bytes this kind of native allocation takes up.  This is used
273      * to help inform the garbage collector about the possible need for
274      * collection. Memory allocated with native malloc is implicitly
275      * included, and ideally should not be included in this argument.
276      * <p>
277      * @param classLoader  ClassLoader that was used to load the native
278      *                     library freeFunction belongs to.
279      * @param clazz        Class that is associated with this registry. If no class is
280      *                     specified, it defaults to NativeAllocationRegistry.class.
281      * @param freeFunction address of a native function used to free this
282      *                     kind of native allocation
283      * @param size         estimated size in bytes of this kind of native
284      *                     allocation. If mallocAllocation is false, then this
285      *                     should ideally exclude memory allocated by system
286      *                     malloc. However including it will simply double-count it,
287      *                     typically resulting in slightly increased GC frequency.
288      *                     If mallocAllocation is true, then this affects only the
289      *                     frequency with which we sample the malloc heap, and debugging
290      *                     tools. In this case a value of zero is commonly used to
291      *                     indicate an unknown non-huge size.
292      * @param mallocAllocation the native object is primarily allocated via malloc.
293      */
NativeAllocationRegistry(@onNull ClassLoader classLoader, @NonNull Class clazz, long freeFunction, long size, boolean mallocAllocation)294     private NativeAllocationRegistry(@NonNull ClassLoader classLoader, @NonNull Class clazz,
295         long freeFunction, long size, boolean mallocAllocation) {
296         if (size < 0) {
297             throw new IllegalArgumentException("Invalid native allocation size: " + size);
298         }
299         this.clazz = Objects.requireNonNull(clazz);
300         this.classLoader = Objects.requireNonNull(classLoader);
301         this.freeFunction = freeFunction;
302         this.size = mallocAllocation ? (size | IS_MALLOCED) : (size & ~IS_MALLOCED);
303 
304         synchronized(NativeAllocationRegistry.class) {
305             registries.put(this, null);
306         }
307     }
308 
309     /**
310      * Constructs a {@link NativeAllocationRegistry} for a particular kind of native
311      * allocation.
312      * <p>
313      * New code should use the preceding factory methods rather than calling this
314      * constructor directly.
315      * <p>
316      * The {@code size} should be an estimate of the total number of
317      * native bytes this kind of native allocation takes up excluding bytes allocated
318      * with system malloc.
319      * This is used to help inform the garbage collector about the possible need for
320      * collection. Memory allocated with native malloc is implicitly included, and
321      * ideally should not be included in this argument.
322      * <p>
323      * @param classLoader  ClassLoader that was used to load the native
324      *                     library {@code freeFunction} belongs to.
325      * @param freeFunction address of a native function used to free this
326      *                     kind of native allocation
327      * @param size         estimated size in bytes of this kind of native
328      *                     allocation, excluding memory allocated with system malloc.
329      *                     A value of 0 indicates that the memory was allocated mainly
330      *                     with malloc.
331      *
332      * @hide
333      */
334     @SystemApi(client = MODULE_LIBRARIES)
NativeAllocationRegistry( @onNull ClassLoader classLoader, long freeFunction, long size)335     public NativeAllocationRegistry(
336         @NonNull ClassLoader classLoader, long freeFunction, long size) {
337         this(classLoader, NativeAllocationRegistry.class, freeFunction, size, size == 0);
338     }
339 
340     private volatile int counter = 0;
341 
342     private static final VarHandle COUNTER;
343     static {
344         try {
345             MethodHandles.Lookup l = MethodHandles.lookup();
346             COUNTER = l.findVarHandle(NativeAllocationRegistry.class,
347                 "counter", int.class);
348         } catch (ReflectiveOperationException e) {
349             throw new ExceptionInInitializerError(e);
350         }
351     }
352 
353     /**
354      * Per-class metrics of native allocations, which includes:
355      *   - class name
356      *   - number and memory used in bytes for native allocations that are
357      *     - registered but not yet released
358      *     - allocated from malloc (malloced) or not from malloc (nonmalloced)
359      *
360      * Metrics from different registries but of the same class will be aggregated.
361      *
362      * @hide
363      */
364     @SystemApi(client = MODULE_LIBRARIES)
365     @FlaggedApi(com.android.libcore.Flags.FLAG_NATIVE_METRICS)
366     public static final class Metrics {
367         private String className;
368         private int mallocedCount;
369         private long mallocedBytes;
370         private int nonmallocedCount;
371         private long nonmallocedBytes;
372 
Metrics(@onNull String className)373         private Metrics(@NonNull String className) {
374             this.className = className;
375         }
376 
add(NativeAllocationRegistry r)377         private void add(NativeAllocationRegistry r) {
378             int count = r.counter;
379             long bytes = count * (r.size & ~IS_MALLOCED);
380             if (r.isMalloced()) {
381                 mallocedCount += count;
382                 mallocedBytes += bytes;
383             } else {
384                 nonmallocedCount += count;
385                 nonmallocedBytes += bytes;
386             }
387         }
388 
389         /**
390          * Returns the name of the class this metrics is associated
391          */
getClassName()392         public @NonNull String getClassName() {
393             return className;
394         }
395 
396         /**
397          * Returns the number of malloced native allocations
398          */
getMallocedCount()399         public long getMallocedCount() {
400             return mallocedCount;
401         }
402 
403         /**
404          * Returns the memory size in bytes of malloced native allocations
405          */
getMallocedBytes()406         public long getMallocedBytes() {
407             return mallocedBytes;
408         }
409 
410         /**
411          * Returns the accounted number of nonmalloced native allocations
412          */
getNonmallocedCount()413         public long getNonmallocedCount() {
414             return nonmallocedCount;
415         }
416 
417         /**
418          * Returns the memory size in bytes of nonmalloced native allocations
419          */
getNonmallocedBytes()420         public long getNonmallocedBytes() {
421             return nonmallocedBytes;
422         }
423     }
424 
425     private static int numClasses = 3;  /* default number of classes with aggregated metrics */
426 
427     /**
428      * Returns per-class metrics in a Collection.
429      *
430      * Metrics of the same class (even through multiple registries) will be aggregated
431      * under the same class name.
432      *
433      * Metrics of the registries with no class explictily specified will be aggregated
434      * under the class name of `libcore.util.NativeAllocationRegistry` by default.
435      *
436      * NOTE:
437      *   1) ArrayList is used here for both memory and performance given
438      *   the number of classes with aggregated metrics is typically small,
439      *   a linear search will be fast enough here
440      *   2) Use the previous number of aggregated classes + 1 to minimize
441      *   memory usage, assuming the number doesn't jump much from last time.
442      *
443      * @hide
444      */
445     @SystemApi(client = MODULE_LIBRARIES)
446     @FlaggedApi(com.android.libcore.Flags.FLAG_NATIVE_METRICS)
getMetrics()447     public static synchronized @NonNull Collection<Metrics> getMetrics() {
448         List<Metrics> result = new ArrayList<>(numClasses + 1);
449         for (NativeAllocationRegistry r : registries.keySet()) {
450             String className = r.clazz.getName();
451             Metrics m = null;
452             for (int i = 0; i < result.size(); i++) {
453                 if (result.get(i).className == className) {
454                     m = result.get(i);
455                     break;
456                 }
457             }
458             if (m == null) {
459                 m = new Metrics(className);
460                 result.add(m);
461             }
462             m.add(r);
463         }
464         numClasses = result.size();
465         return result;
466     }
467 
468     /**
469      * Registers a new native allocation and associated Java object with the
470      * runtime.
471      * This {@link NativeAllocationRegistry}'s {@code freeFunction} will
472      * automatically be called with {@code nativePtr} as its sole
473      * argument when {@code referent} becomes unreachable. If you
474      * maintain copies of {@code nativePtr} outside
475      * {@code referent}, you must not access these after
476      * {@code referent} becomes unreachable, because they may be dangling
477      * pointers.
478      * <p>
479      * The returned Runnable can be used to free the native allocation before
480      * {@code referent} becomes unreachable. The runnable will have no
481      * effect if the native allocation has already been freed by the runtime
482      * or by using the runnable.
483      * <p>
484      * WARNING: This unconditionally takes ownership, i.e. deallocation
485      * responsibility of nativePtr. nativePtr will be DEALLOCATED IMMEDIATELY
486      * if the registration attempt throws an exception (other than one reporting
487      * a programming error).
488      *
489      * @param referent      Non-{@code null} java object to associate the native allocation with
490      * @param nativePtr     Non-zero address of the native allocation
491      * @return runnable to explicitly free native allocation
492      * @throws IllegalArgumentException if either referent or nativePtr is {@code null}.
493      * @throws OutOfMemoryError  if there is not enough space on the Java heap
494      *                           in which to register the allocation. In this
495      *                           case, {@code freeFunction} will be
496      *                           called with {@code nativePtr} as its
497      *                           argument before the {@link OutOfMemoryError} is
498      *                           thrown.
499      *
500      * @hide
501      */
502     @SystemApi(client = MODULE_LIBRARIES)
503     @libcore.api.IntraCoreApi
registerNativeAllocation(@onNull Object referent, long nativePtr)504     public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
505         if (referent == null) {
506             throw new IllegalArgumentException("referent is null");
507         }
508         if (nativePtr == 0) {
509             throw new IllegalArgumentException("nativePtr is null");
510         }
511 
512         CleanerThunk thunk;
513         CleanerRunner result;
514         try {
515             thunk = new CleanerThunk();
516             Cleaner cleaner = Cleaner.create(referent, thunk);
517             result = new CleanerRunner(cleaner);
518             registerNativeAllocation(this.size);
519         } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
520             applyFreeFunction(freeFunction, nativePtr);
521             throw vme;
522         } // Other exceptions are impossible.
523         // Enable the cleaner only after we can no longer throw anything, including OOME.
524         thunk.setNativePtr(nativePtr);
525         // Ensure that cleaner doesn't get invoked before we enable it.
526         Reference.reachabilityFence(referent);
527         COUNTER.getAndAdd(this, 1);
528         return result;
529     }
530 
531     private class CleanerThunk implements Runnable {
532         private long nativePtr;
533 
CleanerThunk()534         public CleanerThunk() {
535             this.nativePtr = 0;
536         }
537 
run()538         public void run() {
539             if (nativePtr != 0) {
540                 applyFreeFunction(freeFunction, nativePtr);
541                 registerNativeFree(size);
542                 COUNTER.getAndAdd(NativeAllocationRegistry.this, -1);
543             }
544         }
545 
setNativePtr(long nativePtr)546         public void setNativePtr(long nativePtr) {
547             this.nativePtr = nativePtr;
548         }
549 
550         // Only for error reporting.
toString()551         @Override public String toString() {
552             return super.toString() + "(freeFunction = 0x" + Long.toHexString(freeFunction)
553                 + ", nativePtr = 0x" + Long.toHexString(nativePtr) + ", size = " + size + ")";
554         }
555     }
556 
557     /**
558      * ReferenceQueueDaemon timeout code needs to identify these for better diagnostics.
559      * @hide
560      */
isCleanerThunk(Object obj)561     public static boolean isCleanerThunk(Object obj) {
562         return obj instanceof CleanerThunk;
563     }
564 
565     private static class CleanerRunner implements Runnable {
566         private final Cleaner cleaner;
567 
CleanerRunner(Cleaner cleaner)568         public CleanerRunner(Cleaner cleaner) {
569             this.cleaner = cleaner;
570         }
571 
run()572         public void run() {
573             cleaner.clean();
574         }
575     }
576 
577     // Inform the garbage collector of the allocation. We do this differently for
578     // malloc-based allocations.
registerNativeAllocation(long size)579     private static void registerNativeAllocation(long size) {
580         VMRuntime runtime = VMRuntime.getRuntime();
581         if ((size & IS_MALLOCED) != 0) {
582             final long notifyImmediateThreshold = 300000;
583             if (size >= notifyImmediateThreshold) {
584                 runtime.notifyNativeAllocationsInternal();
585             } else {
586                 runtime.notifyNativeAllocation();
587             }
588         } else {
589             runtime.registerNativeAllocation(size);
590         }
591     }
592 
593     // Inform the garbage collector of deallocation, if appropriate.
registerNativeFree(long size)594     private static void registerNativeFree(long size) {
595         if ((size & IS_MALLOCED) == 0) {
596             VMRuntime.getRuntime().registerNativeFree(size);
597         }
598     }
599 
600     /**
601      * Calls {@code freeFunction}({@code nativePtr}).
602      * Provided as a convenience in the case where you wish to manually free a
603      * native allocation using a {@code freeFunction} without using a
604      * {@link NativeAllocationRegistry}.
605      *
606      * @param freeFunction address of a native function used to free this
607      *                     kind of native allocation
608      * @param nativePtr    pointer to pass to freeing function
609      *
610      * @hide
611      */
612     @SystemApi(client = MODULE_LIBRARIES)
applyFreeFunction(long freeFunction, long nativePtr)613     public static native void applyFreeFunction(long freeFunction, long nativePtr);
614 }
615 
616