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