diff options
author | Mathieu Chartier <mathieuc@google.com> | 2015-06-05 13:21:05 -0700 |
---|---|---|
committer | Mathieu Chartier <mathieuc@google.com> | 2015-06-05 16:16:38 -0700 |
commit | 3b532d744034b43ed329a3198f15846d80fec3f0 (patch) | |
tree | 05195156ad2d516396d88c8356d4fc9916c089f1 | |
parent | 7fabaa6ba1927d21a317c03499b705cbde4f6a47 (diff) | |
download | art-3b532d744034b43ed329a3198f15846d80fec3f0.zip art-3b532d744034b43ed329a3198f15846d80fec3f0.tar.gz art-3b532d744034b43ed329a3198f15846d80fec3f0.tar.bz2 |
Use runFinalizationWithTimeout for native allocations
Prevents deadlocks by not waiting longer than 250ms for finalizers
to complete.
Bug: 21544853
Change-Id: I57b2f7ae8b74185922eb3c15ba0ab71a4d2348aa
-rw-r--r-- | runtime/base/time_utils.h | 4 | ||||
-rw-r--r-- | runtime/gc/heap.cc | 23 | ||||
-rw-r--r-- | runtime/gc/heap.h | 4 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 5 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 2 | ||||
-rw-r--r-- | test/004-NativeAllocations/src/Main.java | 30 |
6 files changed, 46 insertions, 22 deletions
diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h index f58c22a..55d2764 100644 --- a/runtime/base/time_utils.h +++ b/runtime/base/time_utils.h @@ -68,8 +68,8 @@ static constexpr inline uint64_t NsToMs(uint64_t ns) { } // Converts the given number of milliseconds to nanoseconds -static constexpr inline uint64_t MsToNs(uint64_t ns) { - return ns * 1000 * 1000; +static constexpr inline uint64_t MsToNs(uint64_t ms) { + return ms * 1000 * 1000; } #if defined(__APPLE__) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d344d81..aeab7d8 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -110,6 +110,9 @@ static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB / sizeof(mirror::HeapReference<mirror::Object>); static constexpr size_t kDefaultAllocationStackSize = 8 * MB / sizeof(mirror::HeapReference<mirror::Object>); +// System.runFinalization can deadlock with native allocations, to deal with this, we have a +// timeout on how long we wait for finalizers to run. b/21544853 +static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u); Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, double foreground_heap_growth_multiplier, @@ -3538,22 +3541,16 @@ bool Heap::IsGCRequestPending() const { return concurrent_gc_pending_.LoadRelaxed(); } -void Heap::RunFinalization(JNIEnv* env) { - // Can't do this in WellKnownClasses::Init since System is not properly set up at that point. - if (WellKnownClasses::java_lang_System_runFinalization == nullptr) { - CHECK(WellKnownClasses::java_lang_System != nullptr); - WellKnownClasses::java_lang_System_runFinalization = - CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); - CHECK(WellKnownClasses::java_lang_System_runFinalization != nullptr); - } - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); +void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { + env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_runFinalization, + static_cast<jlong>(timeout)); } void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { Thread* self = ThreadForEnv(env); if (native_need_to_run_finalization_) { - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); UpdateMaxNativeFootprint(); native_need_to_run_finalization_ = false; } @@ -3569,7 +3566,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { if (new_native_bytes_allocated > growth_limit_) { if (WaitForGcToComplete(kGcCauseForNativeAlloc, self) != collector::kGcTypeNone) { // Just finished a GC, attempt to run finalizers. - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); CHECK(!env->ExceptionCheck()); // Native bytes allocated may be updated by finalization, refresh it. new_native_bytes_allocated = native_bytes_allocated_.LoadRelaxed(); @@ -3577,7 +3574,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { // If we still are over the watermark, attempt a GC for alloc and run finalizers. if (new_native_bytes_allocated > growth_limit_) { CollectGarbageInternal(gc_type, kGcCauseForNativeAlloc, false); - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); native_need_to_run_finalization_ = false; CHECK(!env->ExceptionCheck()); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c72414a..81a9741 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -776,8 +776,8 @@ class Heap { bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Run the finalizers. - void RunFinalization(JNIEnv* env); + // Run the finalizers. If timeout is non zero, then we use the VMRuntime version. + void RunFinalization(JNIEnv* env, uint64_t timeout); // Blocks the caller until the garbage collector becomes idle and returns the type of GC we // waited for. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 3dbfe1b..e7857a0 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -34,6 +34,7 @@ jclass WellKnownClasses::dalvik_system_DexFile; jclass WellKnownClasses::dalvik_system_DexPathList; jclass WellKnownClasses::dalvik_system_DexPathList__Element; jclass WellKnownClasses::dalvik_system_PathClassLoader; +jclass WellKnownClasses::dalvik_system_VMRuntime; jclass WellKnownClasses::java_lang_BootClassLoader; jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; @@ -63,6 +64,7 @@ jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; jmethodID WellKnownClasses::com_android_dex_Dex_create; +jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization; jmethodID WellKnownClasses::java_lang_Boolean_valueOf; jmethodID WellKnownClasses::java_lang_Byte_valueOf; jmethodID WellKnownClasses::java_lang_Character_valueOf; @@ -209,6 +211,8 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList"); dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element"); dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader"); + dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime"); + java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader"); java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader"); java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException"); @@ -238,6 +242,7 @@ void WellKnownClasses::Init(JNIEnv* env) { org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); + dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V"); com_android_dex_Dex_create = CacheMethod(env, com_android_dex_Dex, true, "create", "(Ljava/nio/ByteBuffer;)Lcom/android/dex/Dex;"); java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d25d1c3..66b9abe 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -45,6 +45,7 @@ struct WellKnownClasses { static jclass dalvik_system_DexPathList; static jclass dalvik_system_DexPathList__Element; static jclass dalvik_system_PathClassLoader; + static jclass dalvik_system_VMRuntime; static jclass java_lang_BootClassLoader; static jclass java_lang_ClassLoader; static jclass java_lang_ClassNotFoundException; @@ -74,6 +75,7 @@ struct WellKnownClasses { static jclass org_apache_harmony_dalvik_ddmc_DdmServer; static jmethodID com_android_dex_Dex_create; + static jmethodID dalvik_system_VMRuntime_runFinalization; static jmethodID java_lang_Boolean_valueOf; static jmethodID java_lang_Byte_valueOf; static jmethodID java_lang_Character_valueOf; diff --git a/test/004-NativeAllocations/src/Main.java b/test/004-NativeAllocations/src/Main.java index a99fe92..92f4e21 100644 --- a/test/004-NativeAllocations/src/Main.java +++ b/test/004-NativeAllocations/src/Main.java @@ -19,6 +19,8 @@ import java.lang.Runtime; public class Main { static Object nativeLock = new Object(); + static Object deadlockLock = new Object(); + static boolean aboutToDeadlockLock = false; static int nativeBytes = 0; static Object runtime; static Method register_native_allocation; @@ -28,13 +30,15 @@ public class Main { static class NativeAllocation { private int bytes; - NativeAllocation(int bytes) throws Exception { + NativeAllocation(int bytes, boolean testingDeadlock) throws Exception { this.bytes = bytes; register_native_allocation.invoke(runtime, bytes); synchronized (nativeLock) { - nativeBytes += bytes; - if (nativeBytes > maxMem) { - throw new OutOfMemoryError(); + if (!testingDeadlock) { + nativeBytes += bytes; + if (nativeBytes > maxMem) { + throw new OutOfMemoryError(); + } } } } @@ -44,6 +48,9 @@ public class Main { nativeBytes -= bytes; } register_native_free.invoke(runtime, bytes); + aboutToDeadlockLock = true; + synchronized (deadlockLock) { + } } } @@ -59,7 +66,20 @@ public class Main { int allocation_count = 256; NativeAllocation[] allocations = new NativeAllocation[count]; for (int i = 0; i < allocation_count; ++i) { - allocations[i % count] = new NativeAllocation(size); + allocations[i % count] = new NativeAllocation(size, false); + } + // Test that we don't get a deadlock if we are holding nativeLock. If there is no timeout, + // then we will get a finalizer timeout exception. + aboutToDeadlockLock = false; + synchronized (deadlockLock) { + for (int i = 0; aboutToDeadlockLock != true; ++i) { + allocations[i % count] = new NativeAllocation(size, true); + } + // Do more allocations now that the finalizer thread is deadlocked so that we force + // finalization and timeout. + for (int i = 0; i < 10; ++i) { + allocations[i % count] = new NativeAllocation(size, true); + } } System.out.println("Test complete"); } |