Fix local reference leaks in debugger and use a cache.
Changed alloc record stack trace element to use jmethodID instead of
JNI weak global references. Added code to delete the local ref
created in AllocRecord::SetType.
Bug: 15886342
External bug: https://code.google.com/p/android/issues/detail?id=72330
Change-Id: Id18e765820baad02246768dc9d633aada60f4fed
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index f19c353..11415b4 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -74,18 +74,13 @@
}
mirror::ArtMethod* Method() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* method = reinterpret_cast<mirror::ArtMethod*>(
- Thread::Current()->DecodeJObject(method_));
- return method;
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ return soa.DecodeMethod(method_);
}
void SetMethod(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- JNIEnv* env = soa.Env();
- if (method_ != nullptr) {
- env->DeleteWeakGlobalRef(method_);
- }
- method_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(m));
+ method_ = soa.EncodeMethod(m);
}
uint32_t DexPc() const {
@@ -97,27 +92,46 @@
}
private:
- jobject method_; // This is a weak global.
+ jmethodID method_;
uint32_t dex_pc_;
};
+jobject Dbg::TypeCache::Add(mirror::Class* t) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ int32_t hash_code = t->IdentityHashCode();
+ auto range = objects_.equal_range(hash_code);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (soa.Decode<mirror::Class*>(it->second) == t) {
+ // Found a matching weak global, return it.
+ return it->second;
+ }
+ }
+ JNIEnv* env = soa.Env();
+ const jobject local_ref = soa.AddLocalReference<jobject>(t);
+ const jobject weak_global = env->NewWeakGlobalRef(local_ref);
+ env->DeleteLocalRef(local_ref);
+ objects_.insert(std::make_pair(hash_code, weak_global));
+ return weak_global;
+}
+
+void Dbg::TypeCache::Clear() {
+ ScopedObjectAccess soa(Thread::Current());
+ for (const auto& p : objects_) {
+ soa.Vm()->DeleteWeakGlobalRef(soa.Self(), p.second);
+ }
+ objects_.clear();
+}
+
class AllocRecord {
public:
AllocRecord() : type_(nullptr), byte_count_(0), thin_lock_id_(0) {}
mirror::Class* Type() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* type = reinterpret_cast<mirror::Class*>(
- Thread::Current()->DecodeJObject(type_));
- return type;
+ return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
}
void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- JNIEnv* env = soa.Env();
- if (type_ != nullptr) {
- env->DeleteWeakGlobalRef(type_);
- }
- type_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(t));
+ type_ = Dbg::GetTypeCache().Add(t);
}
size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -274,6 +288,7 @@
size_t Dbg::alloc_record_max_ = 0;
size_t Dbg::alloc_record_head_ = 0;
size_t Dbg::alloc_record_count_ = 0;
+Dbg::TypeCache Dbg::type_cache_;
// Deoptimization support.
Mutex* Dbg::deoptimization_lock_ = nullptr;
@@ -4253,8 +4268,10 @@
Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
{
MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
+ LOG(INFO) << "Disabling alloc tracker";
delete[] recent_allocation_records_;
recent_allocation_records_ = NULL;
+ type_cache_.Clear();
}
}
}
@@ -4376,8 +4393,12 @@
StringTable() {
}
- void Add(const char* s) {
- table_.insert(s);
+ void Add(const std::string& str) {
+ table_.insert(str);
+ }
+
+ void Add(const char* str) {
+ table_.insert(str);
}
size_t IndexOf(const char* s) const {
@@ -4476,9 +4497,7 @@
int idx = HeadIndex();
while (count--) {
AllocRecord* record = &recent_allocation_records_[idx];
-
- class_names.Add(record->Type()->GetDescriptor().c_str());
-
+ class_names.Add(record->Type()->GetDescriptor());
for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
mirror::ArtMethod* m = record->StackElement(i)->Method();
if (m != NULL) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 1cf0b0c..2589638 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -23,6 +23,7 @@
#include <pthread.h>
+#include <map>
#include <set>
#include <string>
#include <vector>
@@ -160,6 +161,17 @@
class Dbg {
public:
+ class TypeCache {
+ public:
+ // Returns a weak global for the input type. Deduplicates.
+ jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Clears the type cache and deletes all the weak global refs.
+ void Clear();
+
+ private:
+ std::multimap<int32_t, jobject> objects_;
+ };
+
static bool ParseJdwpOptions(const std::string& options);
static void SetJdwpAllowed(bool allowed);
@@ -555,6 +567,10 @@
static void DdmSendHeapSegments(bool native)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static TypeCache& GetTypeCache() {
+ return type_cache_;
+ }
+
private:
static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
@@ -604,6 +620,9 @@
static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
+ // Weak global type cache, TODO improve this.
+ static TypeCache type_cache_;
+
// Instrumentation event reference counters.
// TODO we could use an array instead of having all these dedicated counters. Instrumentation
// events are bits of a mask so we could convert them to array index.