diff options
author | Hans Boehm <hboehm@google.com> | 2018-10-12 16:51:35 -0700 |
---|---|---|
committer | Hans Boehm <hboehm@google.com> | 2018-12-12 21:11:14 -0800 |
commit | 1ab882b3c38b24c180511a6022dde12774bea794 (patch) | |
tree | bb4ae24a6fb46bb643694d79d6cc936f98be8d61 /libart | |
parent | e3222b5cb448648eba92a8192f6660570040bb21 (diff) |
Adjust NativeAllocationRegistry for new accounting
Provide a new NativeAllocationRegistry constructor that does not
require a size, assuming instead that all native allocation goes
through malloc() and the GC tracks native allocation anyway.
Use it in BigInt, Pattern, and Matcher, the traditional problem cases
for proper size tracking. We should use it in many other cases as well.
The fact that we're not yet doing so has the effect that we're currently
double-counting some allocations. However a mere factor of 2 error is
likely to be a huge improvement already over prior accounting.
Force the argument to setTargetHeapUtilization to be at least 0.1.
Very small numbers are not reasonable, and could cause overflows in ART.
(Needed by accounting cleanups in the companion ART CL.)
Bug: 111447610
Test: atest NativeAllocationRegistryTest (option 2)
Test: Built and booted AOSP. Ran 175-alloc-big-bignums.
Change-Id: Iad76e1efe0849ddf03834571f71a00e684e82858
Diffstat (limited to 'libart')
-rw-r--r-- | libart/src/main/java/dalvik/system/VMRuntime.java | 77 |
1 files changed, 69 insertions, 8 deletions
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java index 4d6b8c7e31..d2f0317b18 100644 --- a/libart/src/main/java/dalvik/system/VMRuntime.java +++ b/libart/src/main/java/dalvik/system/VMRuntime.java @@ -19,6 +19,7 @@ package dalvik.system; import dalvik.annotation.compat.UnsupportedAppUsage; import dalvik.annotation.optimization.FastNative; import java.lang.ref.FinalizerReference; +import java.util.concurrent.atomic.AtomicInteger; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; @@ -67,6 +68,13 @@ public final class VMRuntime { private int targetSdkVersion = SDK_VERSION_CUR_DEVELOPMENT; + // notifyNativeAllocationsInternal (below) should be called every notifyNativeInterval + // allocations. Initialized on demand to allow completely static class initialization. + private int notifyNativeInterval; + + // Allocations since last call to native layer. See notifyNativeAllocation(). + private final AtomicInteger allocationCount = new AtomicInteger(0); + /** * Prevents this class from being instantiated. */ @@ -163,12 +171,14 @@ public final class VMRuntime { @libcore.api.CorePlatformApi public float setTargetHeapUtilization(float newTarget) { if (newTarget <= 0.0f || newTarget >= 1.0f) { - throw new IllegalArgumentException(newTarget + - " out of range (0,1)"); + throw new IllegalArgumentException(newTarget + " out of range (0,1)"); + } + /* The native code assumes a value >= 0.1. Clamp it to that. */ + if (newTarget < 0.1f) { + newTarget = 0.1f; } - /* Synchronize to make sure that only one thread gets - * a given "old" value if both update at the same time. - * Allows for reliable save-and-restore semantics. + /* Synchronize to make sure that only one thread gets a given "old" value if both + * update at the same time. Allows for reliable save-and-restore semantics. */ synchronized (this) { float oldTarget = getTargetHeapUtilization(); @@ -375,18 +385,69 @@ public final class VMRuntime { * function requests a concurrent GC. If the native bytes allocated exceeds a second higher * watermark, it is determined that the application is registering native allocations at an * unusually high rate and a GC is performed inside of the function to prevent memory usage - * from excessively increasing. + * from excessively increasing. Memory allocated via system malloc() should not be included + * in this count. If only malloced() memory is allocated, bytes should be zero. + * The argument must be the same as that later passed to registerNativeFree(), but may + * otherwise be approximate. */ @UnsupportedAppUsage @libcore.api.CorePlatformApi - public native void registerNativeAllocation(int bytes); + public void registerNativeAllocation(int bytes) { + if (bytes == 0) { + notifyNativeAllocation(); + } else { + registerNativeAllocationInternal(bytes); + } + } + + private native void registerNativeAllocationInternal(int bytes); /** * Registers a native free by reducing the number of native bytes accounted for. */ @UnsupportedAppUsage @libcore.api.CorePlatformApi - public native void registerNativeFree(int bytes); + public void registerNativeFree(int bytes) { + if (bytes != 0) { + registerNativeFreeInternal(bytes); + } + } + + private native void registerNativeFreeInternal(int bytes); + + /** + * Return the number of native objects that are reported by a single call to + * notifyNativeAllocation(). + */ + private static native int getNotifyNativeInterval(); + + /** + * Report a native malloc()-only allocation to the GC. + */ + private void notifyNativeAllocation() { + // Minimize JNI calls by notifying once every notifyNativeInterval allocations. + // The native code cannot do anything without calling mallinfo(), which is too + // expensive to perform on every allocation. To avoid the JNI overhead on every + // allocation, we do the sampling here, rather than in native code. + // Initialize notifyNativeInterval carefully. Multiple initializations may race. + int myNotifyNativeInterval = notifyNativeInterval; + if (myNotifyNativeInterval == 0) { + // This can race. By Java rules, that's OK. + myNotifyNativeInterval = notifyNativeInterval = getNotifyNativeInterval(); + } + // myNotifyNativeInterval is correct here. If another thread won the initial race, + // notifyNativeInterval may not be. + if (allocationCount.addAndGet(1) % myNotifyNativeInterval == 0) { + notifyNativeAllocationsInternal(); + } + } + + /** + * Report to the GC that roughly notifyNativeInterval native malloc()-based + * allocations have occurred since the last call to notifyNativeAllocationsInternal(). + * Hints that we should check whether a GC is required. + */ + private native void notifyNativeAllocationsInternal(); /** * Wait for objects to be finalized. |