OSDN Git Service

Fix native allocation tracking on Dalvik.
authorMathieu Chartier <mathieuc@google.com>
Thu, 22 Aug 2013 21:25:55 +0000 (14:25 -0700)
committerMathieu Chartier <mathieuc@google.com>
Fri, 23 Aug 2013 21:18:28 +0000 (14:18 -0700)
Fixed issue where we had inverted logic when checking for
pending exceptions.

Changed the logic to not update the native allocation watermarks
after concurrent GC since the finalizers may not have been run at
this point.

Copied over the test from ART for ensuring that it is working.

Bug: 10444866

Change-Id: Ie26170f9c4703f11e993233cdc9c5f46396fca84

tests/098-native-allocations/expected.txt [new file with mode: 0644]
tests/098-native-allocations/info.txt [new file with mode: 0644]
tests/098-native-allocations/src/Main.java [new file with mode: 0644]
vm/alloc/Alloc.cpp
vm/alloc/HeapSource.cpp

diff --git a/tests/098-native-allocations/expected.txt b/tests/098-native-allocations/expected.txt
new file mode 100644 (file)
index 0000000..f75da10
--- /dev/null
@@ -0,0 +1 @@
+Test complete
diff --git a/tests/098-native-allocations/info.txt b/tests/098-native-allocations/info.txt
new file mode 100644 (file)
index 0000000..2e5b88a
--- /dev/null
@@ -0,0 +1,2 @@
+This is a test to verify that native allocation successfully runs
+finalizers and prevents OOM.
diff --git a/tests/098-native-allocations/src/Main.java b/tests/098-native-allocations/src/Main.java
new file mode 100644 (file)
index 0000000..87179b5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+class Main {
+    static Object nativeLock = new Object();
+    static int nativeBytes = 0;
+    static Object runtime;
+    static Method register_native_allocation;
+    static Method register_native_free;
+    static int maxMem = 64 * 1024 * 1024;
+
+    static class NativeAllocation {
+        private int bytes;
+
+        NativeAllocation(int bytes) throws Exception {
+            this.bytes = bytes;
+            register_native_allocation.invoke(runtime, bytes);
+            synchronized (nativeLock) {
+                nativeBytes += bytes;
+                if (nativeBytes > maxMem) {
+                    throw new OutOfMemoryError();
+                }
+            }
+        }
+
+        protected void finalize() throws Exception {
+            synchronized (nativeLock) {
+                nativeBytes -= bytes;
+            }
+            register_native_free.invoke(runtime, bytes);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
+        Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
+        runtime = get_runtime.invoke(null);
+        register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
+        register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+        int count = 16;
+        int size = 512 * 0x400;
+        int allocation_count = 256;
+        NativeAllocation[] allocations = new NativeAllocation[count];
+        for (int i = 0; i < allocation_count; ++i) {
+            allocations[i % count] = new NativeAllocation(size);
+        }
+        System.out.println("Test complete");
+    }
+}
+
index 8edbbf6..3d40d11 100644 (file)
@@ -315,6 +315,7 @@ void dvmRunFinalization() {
   Thread *self = dvmThreadSelf();
   assert(self != NULL);
   JValue unusedResult;
+  assert(gDvm.methJavaLangSystem_runFinalization != NULL);
   dvmCallMethod(self, gDvm.methJavaLangSystem_runFinalization, NULL, &unusedResult);
 }
 
index 2c0a474..82c2b9e 100644 (file)
@@ -29,6 +29,7 @@
 #include "alloc/HeapBitmap.h"
 #include "alloc/HeapBitmapInlines.h"
 
+static void dvmHeapSourceUpdateMaxNativeFootprint();
 static void snapIdealFootprint();
 static void setIdealFootprint(size_t max);
 static size_t getMaximumSize(const HeapSource *hs);
@@ -183,6 +184,7 @@ struct HeapSource {
     int32_t nativeBytesAllocated;
     size_t nativeFootprintGCWatermark;
     size_t nativeFootprintLimit;
+    bool nativeNeedToRunFinalization;
 
     /*
      * State for the GC daemon.
@@ -648,6 +650,7 @@ GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
     hs->nativeBytesAllocated = 0;
     hs->nativeFootprintGCWatermark = startSize;
     hs->nativeFootprintLimit = startSize * 2;
+    hs->nativeNeedToRunFinalization = false;
     hs->hasGcThread = false;
     hs->heapBase = (char *)base;
     hs->heapLength = length;
@@ -1408,6 +1411,11 @@ void dvmHeapSourceGrowForUtilization()
     } else {
         heap->concurrentStartBytes = freeBytes - CONCURRENT_START;
     }
+
+    /* Mark that we need to run finalizers and update the native watermarks
+     * next time we attempt to register a native allocation.
+     */
+    gHs->nativeNeedToRunFinalization = true;
 }
 
 /*
@@ -1523,6 +1531,15 @@ static void dvmHeapSourceUpdateMaxNativeFootprint()
 
 void dvmHeapSourceRegisterNativeAllocation(int bytes)
 {
+    /* If we have just done a GC, ensure that the finalizers are done and update
+     * the native watermarks.
+     */
+    if (gHs->nativeNeedToRunFinalization) {
+        dvmRunFinalization();
+        dvmHeapSourceUpdateMaxNativeFootprint();
+        gHs->nativeNeedToRunFinalization = false;
+    }
+
     android_atomic_add(bytes, &gHs->nativeBytesAllocated);
 
     if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintGCWatermark) {
@@ -1532,19 +1549,17 @@ void dvmHeapSourceRegisterNativeAllocation(int bytes)
          */
         if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintLimit) {
             Thread* self = dvmThreadSelf();
-
             dvmRunFinalization();
-            if (!dvmCheckException(self)) {
+            if (dvmCheckException(self)) {
                 return;
             }
-
             dvmLockHeap();
             bool waited = dvmWaitForConcurrentGcToComplete();
             dvmUnlockHeap();
             if (waited) {
                 // Just finished a GC, attempt to run finalizers.
                 dvmRunFinalization();
-                if (!dvmCheckException(self)) {
+                if (dvmCheckException(self)) {
                     return;
                 }
             }
@@ -1556,8 +1571,8 @@ void dvmHeapSourceRegisterNativeAllocation(int bytes)
                 dvmCollectGarbageInternal(GC_FOR_MALLOC);
                 dvmUnlockHeap();
                 dvmRunFinalization();
-
-                if (!dvmCheckException(self)) {
+                gHs->nativeNeedToRunFinalization = false;
+                if (dvmCheckException(self)) {
                     return;
                 }
             }