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