summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/base/time_utils.h4
-rw-r--r--runtime/gc/heap.cc42
-rw-r--r--runtime/gc/heap.h4
-rw-r--r--runtime/gc/space/large_object_space.cc34
-rw-r--r--runtime/java_vm_ext.cc7
-rw-r--r--runtime/mem_map.cc24
-rw-r--r--runtime/runtime.cc19
-rw-r--r--runtime/well_known_classes.cc5
-rw-r--r--runtime/well_known_classes.h2
-rw-r--r--sigchainlib/version-script.txt1
-rw-r--r--test/004-NativeAllocations/src/Main.java30
11 files changed, 132 insertions, 40 deletions
diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h
index f58c22a7cc..55d2764576 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 f039f6beb3..aeab7d80b4 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,
@@ -232,10 +235,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
}
if (!image_file_name.empty()) {
+ ATRACE_BEGIN("ImageSpace::Create");
std::string error_msg;
- space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
- image_instruction_set,
- &error_msg);
+ auto* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set,
+ &error_msg);
+ ATRACE_END();
if (image_space != nullptr) {
AddSpace(image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
@@ -287,6 +291,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
}
std::string error_str;
std::unique_ptr<MemMap> non_moving_space_mem_map;
+ ATRACE_BEGIN("Create heap maps");
if (separate_non_moving_space) {
// If we are the zygote, the non moving space becomes the zygote space when we run
// PreZygoteFork the first time. In this case, call the map "zygote space" since we can't
@@ -323,6 +328,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
capacity_, &error_str));
CHECK(main_mem_map_2.get() != nullptr) << error_str;
}
+ ATRACE_END();
+ ATRACE_BEGIN("Create spaces");
// Create the non moving space first so that bitmaps don't take up the address range.
if (separate_non_moving_space) {
// Non moving space is always dlmalloc since we currently don't have support for multiple
@@ -340,7 +347,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
if (foreground_collector_type_ == kCollectorTypeCC) {
region_space_ = space::RegionSpace::Create("Region space", capacity_ * 2, request_begin);
AddSpace(region_space_);
- } else if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
+ } else if (IsMovingGc(foreground_collector_type_) &&
+ foreground_collector_type_ != kCollectorTypeGSS) {
// Create bump pointer spaces.
// We only to create the bump pointer if the foreground collector is a compacting GC.
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
@@ -411,10 +419,12 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
if (main_space_backup_.get() != nullptr) {
RemoveSpace(main_space_backup_.get());
}
+ ATRACE_END();
// Allocate the card table.
+ ATRACE_BEGIN("Create card table");
card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
CHECK(card_table_.get() != nullptr) << "Failed to create card table";
-
+ ATRACE_END();
if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) {
rb_table_.reset(new accounting::ReadBarrierTable());
DCHECK(rb_table_->IsAllCleared());
@@ -2252,8 +2262,8 @@ void Heap::PreZygoteFork() {
// Set all the cards in the mod-union table since we don't know which objects contain references
// to large objects.
mod_union_table->SetCards();
- large_object_space_->SetAllLargeObjectsAsZygoteObjects(self);
AddModUnionTable(mod_union_table);
+ large_object_space_->SetAllLargeObjectsAsZygoteObjects(self);
if (collector::SemiSpace::kUseRememberedSet) {
// Add a new remembered set for the post-zygote non-moving space.
accounting::RememberedSet* post_zygote_non_moving_space_rem_set =
@@ -3531,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;
}
@@ -3562,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();
@@ -3570,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 c72414a1ab..81a97414ba 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/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 9436c3f700..52192e212c 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -46,8 +46,8 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace {
}
}
- virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size, size_t* bytes_tl_bulk_allocated)
+ mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size, size_t* bytes_tl_bulk_allocated)
OVERRIDE {
mirror::Object* obj =
LargeObjectMapSpace::Alloc(self, num_bytes + kValgrindRedZoneBytes * 2, bytes_allocated,
@@ -63,26 +63,35 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace {
return object_without_rdz;
}
- virtual size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE {
- mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
- reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
- return LargeObjectMapSpace::AllocationSize(object_with_rdz, usable_size);
+ size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE {
+ return LargeObjectMapSpace::AllocationSize(ObjectWithRedzone(obj), usable_size);
}
- virtual size_t Free(Thread* self, mirror::Object* obj) OVERRIDE {
- mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
- reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+ bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE {
+ return LargeObjectMapSpace::IsZygoteLargeObject(self, ObjectWithRedzone(obj));
+ }
+
+ size_t Free(Thread* self, mirror::Object* obj) OVERRIDE {
+ mirror::Object* object_with_rdz = ObjectWithRedzone(obj);
VALGRIND_MAKE_MEM_UNDEFINED(object_with_rdz, AllocationSize(obj, nullptr));
return LargeObjectMapSpace::Free(self, object_with_rdz);
}
bool Contains(const mirror::Object* obj) const OVERRIDE {
- mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>(
- reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
- return LargeObjectMapSpace::Contains(object_with_rdz);
+ return LargeObjectMapSpace::Contains(ObjectWithRedzone(obj));
}
private:
+ static const mirror::Object* ObjectWithRedzone(const mirror::Object* obj) {
+ return reinterpret_cast<const mirror::Object*>(
+ reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+ }
+
+ static mirror::Object* ObjectWithRedzone(mirror::Object* obj) {
+ return reinterpret_cast<mirror::Object*>(
+ reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes);
+ }
+
static constexpr size_t kValgrindRedZoneBytes = kPageSize;
};
@@ -253,6 +262,7 @@ class AllocationInfo {
}
// Updates the allocation size and whether or not it is free.
void SetByteSize(size_t size, bool free) {
+ DCHECK_EQ(size & ~kFlagsMask, 0u);
DCHECK_ALIGNED(size, FreeListSpace::kAlignment);
alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0u);
}
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 2d3d19ce3e..eb9c32d7ad 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -16,6 +16,8 @@
#include "jni_internal.h"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <cutils/trace.h>
#include <dlfcn.h>
#include "art_method.h"
@@ -788,9 +790,11 @@ void JavaVMExt::VisitRoots(RootVisitor* visitor) {
// JNI Invocation interface.
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+ ATRACE_BEGIN(__FUNCTION__);
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
if (IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
+ ATRACE_END();
return JNI_EVERSION;
}
RuntimeOptions options;
@@ -800,6 +804,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
}
bool ignore_unrecognized = args->ignoreUnrecognized;
if (!Runtime::Create(options, ignore_unrecognized)) {
+ ATRACE_END();
return JNI_ERR;
}
Runtime* runtime = Runtime::Current();
@@ -808,10 +813,12 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
delete Thread::Current()->GetJniEnv();
delete runtime->GetJavaVM();
LOG(WARNING) << "CreateJavaVM failed";
+ ATRACE_END();
return JNI_ERR;
}
*p_env = Thread::Current()->GetJniEnv();
*p_vm = runtime->GetJavaVM();
+ ATRACE_END();
return JNI_OK;
}
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index cf4233c3d1..7feac8ab0c 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -314,7 +314,31 @@ MemMap* MemMap::MapAnonymous(const char* name, uint8_t* expected_ptr, size_t byt
if (low_4gb && expected_ptr == nullptr) {
bool first_run = true;
+ MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
for (uintptr_t ptr = next_mem_pos_; ptr < 4 * GB; ptr += kPageSize) {
+ // Use maps_ as an optimization to skip over large maps.
+ // Find the first map which is address > ptr.
+ auto it = maps_->upper_bound(reinterpret_cast<void*>(ptr));
+ if (it != maps_->begin()) {
+ auto before_it = it;
+ --before_it;
+ // Start at the end of the map before the upper bound.
+ ptr = std::max(ptr, reinterpret_cast<uintptr_t>(before_it->second->BaseEnd()));
+ CHECK_ALIGNED(ptr, kPageSize);
+ }
+ while (it != maps_->end()) {
+ // How much space do we have until the next map?
+ size_t delta = reinterpret_cast<uintptr_t>(it->first) - ptr;
+ // If the space may be sufficient, break out of the loop.
+ if (delta >= page_aligned_byte_count) {
+ break;
+ }
+ // Otherwise, skip to the end of the map.
+ ptr = reinterpret_cast<uintptr_t>(it->second->BaseEnd());
+ CHECK_ALIGNED(ptr, kPageSize);
+ ++it;
+ }
+
if (4U * GB - ptr < page_aligned_byte_count) {
// Not enough memory until 4GB.
if (first_run) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 65ea77ad29..9d651bf6a4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -22,6 +22,8 @@
#include <linux/fs.h>
#endif
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <cutils/trace.h>
#include <signal.h>
#include <sys/syscall.h>
#include <valgrind.h>
@@ -492,8 +494,12 @@ bool Runtime::Start() {
ScopedObjectAccess soa(self);
gc::space::ImageSpace* image_space = heap_->GetImageSpace();
if (image_space != nullptr) {
+ ATRACE_BEGIN("AddImageStringsToTable");
GetInternTable()->AddImageStringsToTable(image_space);
+ ATRACE_END();
+ ATRACE_BEGIN("MoveImageClassesToClassTable");
GetClassLinker()->MoveImageClassesToClassTable();
+ ATRACE_END();
}
}
@@ -512,7 +518,9 @@ bool Runtime::Start() {
// InitNativeMethods needs to be after started_ so that the classes
// it touches will have methods linked to the oat file if necessary.
+ ATRACE_BEGIN("InitNativeMethods");
InitNativeMethods();
+ ATRACE_END();
// Initialize well known thread group values that may be accessed threads while attaching.
InitThreadGroups(self);
@@ -533,7 +541,9 @@ bool Runtime::Start() {
GetInstructionSetString(kRuntimeISA));
}
+ ATRACE_BEGIN("StartDaemonThreads");
StartDaemonThreads();
+ ATRACE_END();
{
ScopedObjectAccess soa(self);
@@ -763,6 +773,7 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
}
bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
+ ATRACE_BEGIN("Runtime::Init");
CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
MemMap::Init();
@@ -773,6 +784,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
ParsedOptions::Create(raw_options, ignore_unrecognized, &runtime_options));
if (parsed_options.get() == nullptr) {
LOG(ERROR) << "Failed to parse options";
+ ATRACE_END();
return false;
}
VLOG(startup) << "Runtime::Init -verbose:startup enabled";
@@ -826,6 +838,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
+ ATRACE_BEGIN("CreateHeap");
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -855,9 +868,11 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
xgc_option.verify_post_gc_rosalloc_,
runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
+ ATRACE_END();
if (heap_->GetImageSpace() == nullptr && !allow_dex_file_fallback_) {
LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";
+ ATRACE_END();
return false;
}
@@ -957,7 +972,9 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
class_linker_ = new ClassLinker(intern_table_);
if (GetHeap()->HasImageSpace()) {
+ ATRACE_BEGIN("InitFromImage");
class_linker_->InitFromImage();
+ ATRACE_END();
if (kIsDebugBuild) {
GetHeap()->GetImageSpace()->VerifyImageAllocations();
}
@@ -1090,6 +1107,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
VLOG(startup) << "Runtime::Init exiting";
+ ATRACE_END();
+
return true;
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3dbfe1b516..e7857a0602 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 d25d1c3f7d..66b9abece7 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/sigchainlib/version-script.txt b/sigchainlib/version-script.txt
index ce1505490b..08c312e7e2 100644
--- a/sigchainlib/version-script.txt
+++ b/sigchainlib/version-script.txt
@@ -5,6 +5,7 @@ global:
InvokeUserSignalHandler;
InitializeSignalChain;
EnsureFrontOfChain;
+ SetSpecialSignalHandlerFn;
sigaction;
signal;
sigprocmask;
diff --git a/test/004-NativeAllocations/src/Main.java b/test/004-NativeAllocations/src/Main.java
index a99fe92081..92f4e21f40 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");
}