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