Merge "Adjust NativeAllocationRegistry for new accounting"
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 4d6b8c7e..d2f0317 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -19,6 +19,7 @@
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 @@
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 @@
@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)");
}
- /* 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.
+ /* 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.
*/
synchronized (this) {
float oldTarget = getTargetHeapUtilization();
@@ -375,18 +385,69 @@
* 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.
diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java
index 9faac81..de4cdd8 100644
--- a/luni/src/main/java/java/math/BigInt.java
+++ b/luni/src/main/java/java/math/BigInt.java
@@ -27,7 +27,7 @@
final class BigInt {
private static NativeAllocationRegistry registry = new NativeAllocationRegistry(
- BigInt.class.getClassLoader(), NativeBN.getNativeFinalizer(), NativeBN.size());
+ BigInt.class.getClassLoader(), NativeBN.getNativeFinalizer());
/* Fields used for the internal representation. */
@ReachabilitySensitive
diff --git a/luni/src/main/java/java/math/NativeBN.java b/luni/src/main/java/java/math/NativeBN.java
index d269f2e..ab9b2e0 100644
--- a/luni/src/main/java/java/math/NativeBN.java
+++ b/luni/src/main/java/java/math/NativeBN.java
@@ -133,10 +133,4 @@
public static native long getNativeFinalizer();
// &BN_free
- /** Returns the expected size of the native allocation for a BIGNUM.
- */
- public static long size() {
- // 36 bytes is an empirically determined approximation.
- return 36;
- }
}
diff --git a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
index c1b4fe0..5c8e831 100644
--- a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
+++ b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
@@ -65,13 +65,21 @@
* native bytes this kind of native allocation takes up. Different
* NativeAllocationRegistrys must be used to register native allocations
* with different estimated sizes, even if they use the same
- * <code>freeFunction</code>.
+ * <code>freeFunction</code>. This is used to help inform the garbage
+ * collector about the possible need for collection. Memory allocated with
+ * native malloc is implicitly included, and ideally should not be included in this
+ * argument. For malloc-based native allocations that are expected to be under
+ * 100Kbytes or so, the NativeAllocationRegistry(ClassLoader,long) constructor is
+ * preferred.
+ * <p>
* @param classLoader ClassLoader that was used to load the native
* library freeFunction belongs to.
* @param freeFunction address of a native function used to free this
* kind of native allocation
* @param size estimated size in bytes of this kind of native
- * allocation
+ * allocation. Should ideally exclude memory allocated by system
+ * malloc. However including it will simply double-count it,
+ * typically resulting in slightly increased GC frequency.
* @throws IllegalArgumentException If <code>size</code> is negative
*/
@libcore.api.CorePlatformApi
@@ -79,13 +87,20 @@
if (size < 0) {
throw new IllegalArgumentException("Invalid native allocation size: " + size);
}
-
this.classLoader = classLoader;
this.freeFunction = freeFunction;
this.size = size;
}
/**
+ * Equivalent to NativeAllocationRegistry(classLoader, freeFunction, 0).
+ * Should be used when all native memory is allocated via system malloc.
+ */
+ public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction) {
+ this(classLoader, freeFunction, 0);
+ }
+
+ /**
* Registers a new native allocation and associated Java object with the
* runtime.
* This NativeAllocationRegistry's <code>freeFunction</code> will
@@ -223,6 +238,8 @@
}
}
+ // Inform the garbage collector of the allocation. The size = 0 case is optimized in
+ // VMRuntime.
// TODO: Change the runtime to support passing the size as a long instead
// of an int. For now, we clamp the size to fit.
private static void registerNativeAllocation(long size) {
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 47d8b80..0481722 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -158,16 +158,6 @@
return reinterpret_cast<jlong>(&Matcher_free);
}
-// Return a guess of the amount of native memory to be deallocated by a typical call to
-// Matcher_free().
-static jint Matcher_nativeSize(JNIEnv*, jclass) {
- // This value can be tuned. 200 was found to cause performance issues (b/111141123) so 10000
- // is being used instead. If the value is too small, there's no pressure to collect matchers,
- // too large and we'll run GC too often.
- // Also see b/111141123.
- return 10000;
-}
-
static jint Matcher_findImpl(JNIEnv* env, jclass, jlong addr, jint startIndex, jintArray offsets) {
MatcherState* state = toMatcherState(addr);
UBool result = state->matcher()->find(startIndex, state->status());
@@ -273,7 +263,6 @@
NATIVE_METHOD(Matcher, hitEndImpl, "(J)Z"),
NATIVE_METHOD(Matcher, lookingAtImpl, "(J[I)Z"),
NATIVE_METHOD(Matcher, matchesImpl, "(J[I)Z"),
- NATIVE_METHOD(Matcher, nativeSize, "()I"),
NATIVE_METHOD(Matcher, openImpl, "(J)J"),
NATIVE_METHOD(Matcher, requireEndImpl, "(J)Z"),
NATIVE_METHOD(Matcher, setInputImpl, "(JLjava/lang/String;II)V"),
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
index f4d23cb..ac13346 100644
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ b/luni/src/main/native/java_util_regex_Pattern.cpp
@@ -75,12 +75,6 @@
return reinterpret_cast<jlong>(&Pattern_free);
}
-// Return a guess of the amount of native memory to be deallocated by a typical call to
-// Pattern_free().
-static jint Pattern_nativeSize(JNIEnv*, jclass) {
- return 500; // Very rough guess based on a quick look at the implementation.
-}
-
static jlong Pattern_compileImpl(JNIEnv* env, jclass, jstring javaRegex, jint flags) {
flags |= UREGEX_ERROR_ON_UNKNOWN_ESCAPES;
@@ -103,7 +97,6 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Pattern, compileImpl, "(Ljava/lang/String;I)J"),
NATIVE_METHOD(Pattern, getNativeFinalizer, "()J"),
- NATIVE_METHOD(Pattern, nativeSize, "()I"),
};
void register_java_util_regex_Pattern(JNIEnv* env) {
diff --git a/ojluni/src/main/java/java/util/regex/Matcher.java b/ojluni/src/main/java/java/util/regex/Matcher.java
index e92cb9d..edeecf3 100644
--- a/ojluni/src/main/java/java/util/regex/Matcher.java
+++ b/ojluni/src/main/java/java/util/regex/Matcher.java
@@ -150,7 +150,7 @@
private Runnable nativeFinalizer;
private static final NativeAllocationRegistry registry = new NativeAllocationRegistry(
- Matcher.class.getClassLoader(), getNativeFinalizer(), nativeSize());
+ Matcher.class.getClassLoader(), getNativeFinalizer());
/**
* The index of the last position appended in a substitution.
@@ -1227,7 +1227,6 @@
private static native boolean hitEndImpl(long addr);
private static native boolean lookingAtImpl(long addr, int[] offsets);
private static native boolean matchesImpl(long addr, int[] offsets);
- private static native int nativeSize();
private static native long openImpl(long patternAddr);
private static native boolean requireEndImpl(long addr);
private static native void setInputImpl(long addr, String s, int start, int end);
diff --git a/ojluni/src/main/java/java/util/regex/Pattern.java b/ojluni/src/main/java/java/util/regex/Pattern.java
index 04f4f80..437382b 100644
--- a/ojluni/src/main/java/java/util/regex/Pattern.java
+++ b/ojluni/src/main/java/java/util/regex/Pattern.java
@@ -949,7 +949,7 @@
transient long address;
private static final NativeAllocationRegistry registry = new NativeAllocationRegistry(
- Pattern.class.getClassLoader(), getNativeFinalizer(), nativeSize());
+ Pattern.class.getClassLoader(), getNativeFinalizer());
// END Android-changed: reimplement matching logic natively via ICU.
/**
@@ -1435,7 +1435,6 @@
private static native long compileImpl(String regex, int flags);
private static native long getNativeFinalizer();
- private static native int nativeSize();
// END Android-changed: reimplement matching logic natively via ICU.
/**