/* * 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. */ #include "object_registry.h" #include "handle_scope-inl.h" #include "mirror/class.h" #include "scoped_thread_state_change.h" namespace art { mirror::Object* const ObjectRegistry::kInvalidObject = reinterpret_cast<mirror::Object*>(1); std::ostream& operator<<(std::ostream& os, const ObjectRegistryEntry& rhs) { os << "ObjectRegistryEntry[" << rhs.jni_reference_type << ",reference=" << rhs.jni_reference << ",count=" << rhs.reference_count << ",id=" << rhs.id << "]"; return os; } ObjectRegistry::ObjectRegistry() : lock_("ObjectRegistry lock", kJdwpObjectRegistryLock), next_id_(1) { } JDWP::RefTypeId ObjectRegistry::AddRefType(mirror::Class* c) { return InternalAdd(c); } JDWP::ObjectId ObjectRegistry::Add(mirror::Object* o) { return InternalAdd(o); } JDWP::ObjectId ObjectRegistry::InternalAdd(mirror::Object* o) { if (o == nullptr) { return 0; } Thread* const self = Thread::Current(); StackHandleScope<1> hs(self); Handle<mirror::Object> obj_h(hs.NewHandle(o)); // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock. int32_t identity_hash_code = obj_h->IdentityHashCode(); ScopedObjectAccessUnchecked soa(self); MutexLock mu(soa.Self(), lock_); ObjectRegistryEntry* entry = nullptr; if (ContainsLocked(soa.Self(), obj_h.Get(), identity_hash_code, &entry)) { // This object was already in our map. ++entry->reference_count; } else { entry = new ObjectRegistryEntry; entry->jni_reference_type = JNIWeakGlobalRefType; entry->jni_reference = nullptr; entry->reference_count = 0; entry->id = 0; entry->identity_hash_code = identity_hash_code; object_to_entry_.insert(std::make_pair(identity_hash_code, entry)); // This object isn't in the registry yet, so add it. JNIEnv* env = soa.Env(); jobject local_reference = soa.AddLocalReference<jobject>(obj_h.Get()); entry->jni_reference_type = JNIWeakGlobalRefType; entry->jni_reference = env->NewWeakGlobalRef(local_reference); entry->reference_count = 1; entry->id = next_id_++; id_to_entry_.Put(entry->id, entry); env->DeleteLocalRef(local_reference); } return entry->id; } bool ObjectRegistry::ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code, ObjectRegistryEntry** out_entry) { DCHECK(o != nullptr); for (auto it = object_to_entry_.lower_bound(identity_hash_code), end = object_to_entry_.end(); it != end && it->first == identity_hash_code; ++it) { ObjectRegistryEntry* entry = it->second; if (o == self->DecodeJObject(entry->jni_reference)) { if (out_entry != nullptr) { *out_entry = entry; } return true; } } return false; } void ObjectRegistry::Clear() { Thread* self = Thread::Current(); MutexLock mu(self, lock_); VLOG(jdwp) << "Object registry contained " << object_to_entry_.size() << " entries"; // Delete all the JNI references. JNIEnv* env = self->GetJniEnv(); for (const auto& pair : object_to_entry_) { const ObjectRegistryEntry* entry = pair.second; if (entry->jni_reference_type == JNIWeakGlobalRefType) { env->DeleteWeakGlobalRef(entry->jni_reference); } else { env->DeleteGlobalRef(entry->jni_reference); } delete entry; } // Clear the maps. object_to_entry_.clear(); id_to_entry_.clear(); } mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); if (it == id_to_entry_.end()) { return kInvalidObject; } ObjectRegistryEntry& entry = *it->second; return self->DecodeJObject(entry.jni_reference); } jobject ObjectRegistry::GetJObject(JDWP::ObjectId id) { if (id == 0) { return NULL; } Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); CHECK(it != id_to_entry_.end()) << id; ObjectRegistryEntry& entry = *it->second; return entry.jni_reference; } void ObjectRegistry::DisableCollection(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); CHECK(it != id_to_entry_.end()); Promote(*it->second); } void ObjectRegistry::EnableCollection(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); CHECK(it != id_to_entry_.end()); Demote(*it->second); } void ObjectRegistry::Demote(ObjectRegistryEntry& entry) { if (entry.jni_reference_type == JNIGlobalRefType) { Thread* self = Thread::Current(); JNIEnv* env = self->GetJniEnv(); jobject global = entry.jni_reference; entry.jni_reference = env->NewWeakGlobalRef(entry.jni_reference); entry.jni_reference_type = JNIWeakGlobalRefType; env->DeleteGlobalRef(global); } } void ObjectRegistry::Promote(ObjectRegistryEntry& entry) { if (entry.jni_reference_type == JNIWeakGlobalRefType) { Thread* self = Thread::Current(); JNIEnv* env = self->GetJniEnv(); jobject weak = entry.jni_reference; entry.jni_reference = env->NewGlobalRef(entry.jni_reference); entry.jni_reference_type = JNIGlobalRefType; env->DeleteWeakGlobalRef(weak); } } bool ObjectRegistry::IsCollected(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); CHECK(it != id_to_entry_.end()); ObjectRegistryEntry& entry = *it->second; if (entry.jni_reference_type == JNIWeakGlobalRefType) { JNIEnv* env = self->GetJniEnv(); return env->IsSameObject(entry.jni_reference, NULL); // Has the jweak been collected? } else { return false; // We hold a strong reference, so we know this is live. } } void ObjectRegistry::DisposeObject(JDWP::ObjectId id, uint32_t reference_count) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); auto it = id_to_entry_.find(id); if (it == id_to_entry_.end()) { return; } ObjectRegistryEntry* entry = it->second; entry->reference_count -= reference_count; if (entry->reference_count <= 0) { JNIEnv* env = self->GetJniEnv(); // Erase the object from the maps. Note object may be null if it's // a weak ref and the GC has cleared it. int32_t hash_code = entry->identity_hash_code; for (auto it = object_to_entry_.lower_bound(hash_code), end = object_to_entry_.end(); it != end && it->first == hash_code; ++it) { if (entry == it->second) { object_to_entry_.erase(it); break; } } if (entry->jni_reference_type == JNIWeakGlobalRefType) { env->DeleteWeakGlobalRef(entry->jni_reference); } else { env->DeleteGlobalRef(entry->jni_reference); } id_to_entry_.erase(id); delete entry; } } } // namespace art