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