Don't scan image space when starting runtime.
Bug 10432288.
Find Classes and Strings from dex caches lazily rather than when the image is
loaded.
Make class status changes do notifies when there can be waiters.
For Class lookup there's a pathology if we always search dex caches and
so after 1000 failures move all classes into the class table.
Be consistent in using "const char*" for class linker descriptors as this
most easily agrees with the type in the dex file.
Improve the intern run-test so that it has a case of a literal contained in the
image.
Modify image_test to allow any valid lock word rather than expecting 0, ideally
we wouldn't see inflated monitors but we do due to NotifyAll (see bug 6961405).
Change-Id: Ia9bfa748eeccb9b4498784b97c6823141b1f6db8
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3387a70..cd4a720 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -187,6 +187,8 @@
ClassLinker::ClassLinker(InternTable* intern_table)
// dex_lock_ is recursive as it may be used in stack dumping.
: dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
+ dex_cache_image_class_lookup_required_(false),
+ failed_dex_cache_class_lookups_(0),
class_roots_(NULL),
array_iftable_(NULL),
init_done_(false),
@@ -225,7 +227,7 @@
CHECK(java_lang_Object.get() != NULL);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.get());
- java_lang_Object->SetStatus(mirror::Class::kStatusLoaded);
+ java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self);
// Object[] next to hold class roots.
SirtRef<mirror::Class> object_array_class(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::Class)));
@@ -243,7 +245,7 @@
SirtRef<mirror::Class> java_lang_String(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::StringClass)));
mirror::String::SetClass(java_lang_String.get());
java_lang_String->SetObjectSize(sizeof(mirror::String));
- java_lang_String->SetStatus(mirror::Class::kStatusResolved);
+ java_lang_String->SetStatus(mirror::Class::kStatusResolved, self);
// Create storage for root classes, save away our work so far (requires descriptors).
class_roots_ = mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.get(), kClassRootsMax);
@@ -281,7 +283,7 @@
java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::DexCacheClass)));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get());
java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCacheClass));
- java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved);
+ java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
// Constructor, Field, Method, and AbstractMethod are necessary so that FindClass can link members.
SirtRef<mirror::Class> java_lang_reflect_ArtField(self, AllocClass(self, java_lang_Class.get(),
@@ -289,7 +291,7 @@
CHECK(java_lang_reflect_ArtField.get() != NULL);
java_lang_reflect_ArtField->SetObjectSize(sizeof(mirror::ArtField));
SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.get());
- java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved);
+ java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtField::SetClass(java_lang_reflect_ArtField.get());
SirtRef<mirror::Class> java_lang_reflect_ArtMethod(self, AllocClass(self, java_lang_Class.get(),
@@ -297,7 +299,7 @@
CHECK(java_lang_reflect_ArtMethod.get() != NULL);
java_lang_reflect_ArtMethod->SetObjectSize(sizeof(mirror::ArtMethod));
SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.get());
- java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved);
+ java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.get());
@@ -334,15 +336,15 @@
SetClassRoot(kPrimitiveChar, char_class.get()); // needs descriptor
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
- java_lang_Object->SetStatus(mirror::Class::kStatusNotReady);
+ java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
CHECK_EQ(java_lang_Object.get(), Object_class);
CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
- java_lang_String->SetStatus(mirror::Class::kStatusNotReady);
+ java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* String_class = FindSystemClass("Ljava/lang/String;");
CHECK_EQ(java_lang_String.get(), String_class);
CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
- java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady);
+ java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* DexCache_class = FindSystemClass("Ljava/lang/DexCache;");
CHECK_EQ(java_lang_String.get(), String_class);
CHECK_EQ(java_lang_DexCache.get(), DexCache_class);
@@ -397,15 +399,15 @@
CHECK_EQ(java_lang_Cloneable, kh.GetDirectInterface(0));
CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1));
// Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
- // dex_cache_ fields and register them in classes_.
+ // dex_cache_ fields and register them in class_table_.
mirror::Class* Class_class = FindSystemClass("Ljava/lang/Class;");
CHECK_EQ(java_lang_Class.get(), Class_class);
- java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady);
+ java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Art_method_class = FindSystemClass("Ljava/lang/reflect/ArtMethod;");
CHECK_EQ(java_lang_reflect_ArtMethod.get(), Art_method_class);
- java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady);
+ java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Art_field_class = FindSystemClass("Ljava/lang/reflect/ArtField;");
CHECK_EQ(java_lang_reflect_ArtField.get(), Art_field_class);
@@ -984,21 +986,14 @@
return oat_file;
}
-static void InitFromImageCallbackCommon(mirror::Object* obj, ClassLinker* class_linker,
- bool interpret_only_mode)
+static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
+
DCHECK(obj != NULL);
DCHECK(class_linker != NULL);
- if (obj->GetClass()->IsStringClass()) {
- class_linker->GetInternTable()->RegisterStrong(obj->AsString());
- } else if (obj->IsClass()) {
- // Restore class to ClassLinker::classes_ table.
- mirror::Class* klass = obj->AsClass();
- ClassHelper kh(klass, class_linker);
- mirror::Class* existing = class_linker->InsertClass(kh.GetDescriptor(), klass, true);
- DCHECK(existing == NULL) << kh.GetDescriptor();
- } else if (interpret_only_mode && obj->IsArtMethod()) {
+ if (obj->IsArtMethod()) {
mirror::ArtMethod* method = obj->AsArtMethod();
if (!method->IsNative()) {
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -1009,24 +1004,13 @@
}
}
-static void InitFromImageCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
- InitFromImageCallbackCommon(obj, class_linker, false);
-}
-
-static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
- InitFromImageCallbackCommon(obj, class_linker, true);
-}
-
void ClassLinker::InitFromImage() {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
gc::Heap* heap = Runtime::Current()->GetHeap();
gc::space::ImageSpace* space = heap->GetImageSpace();
+ dex_cache_image_class_lookup_required_ = true;
CHECK(space != NULL);
OatFile& oat_file = GetImageOatFile(space);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
@@ -1049,7 +1033,7 @@
CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
Thread* self = Thread::Current();
- for (int i = 0; i < dex_caches->GetLength(); i++) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
SirtRef<mirror::DexCache> dex_cache(self, dex_caches->Get(i));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location);
@@ -1069,15 +1053,11 @@
// bitmap walk.
mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
- // reinit clases_ table
- {
+ // Set entry point to interpreter if in InterpretOnly mode.
+ if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
heap->FlushAllocStack();
- if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
- heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this);
- } else {
- heap->GetLiveBitmap()->Walk(InitFromImageCallback, this);
- }
+ heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this);
}
// reinit class_roots_
@@ -1120,7 +1100,7 @@
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
- for (const std::pair<size_t, mirror::Class*>& it : classes_) {
+ for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
visitor(it.second, arg);
}
@@ -1134,14 +1114,12 @@
}
}
-void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const {
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (const std::pair<size_t, mirror::Class*>& it : classes_) {
- if (!visitor(it.second, arg)) {
- return;
- }
+void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
+ if (dex_cache_image_class_lookup_required_) {
+ MoveImageClassesToClassTable();
}
- for (const std::pair<size_t, mirror::Class*>& it : image_classes_) {
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
if (!visitor(it.second, arg)) {
return;
}
@@ -1154,7 +1132,7 @@
return true;
}
-void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) const {
+void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
std::set<mirror::Class*> classes;
VisitClasses(GetClassesVisitor, &classes);
for (mirror::Class* klass : classes) {
@@ -1274,7 +1252,7 @@
// Check for circular dependencies between classes.
if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(klass);
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return NULL;
}
// Wait for the pending initialization to complete.
@@ -1383,26 +1361,26 @@
return NULL;
}
-mirror::Class* ClassLinker::DefineClass(const StringPiece& descriptor,
+mirror::Class* ClassLinker::DefineClass(const char* descriptor,
mirror::ClassLoader* class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
Thread* self = Thread::Current();
SirtRef<mirror::Class> klass(self, NULL);
// Load the class from the dex file.
- if (!init_done_) {
+ if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
- if (descriptor == "Ljava/lang/Object;") {
+ if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.reset(GetClassRoot(kJavaLangObject));
- } else if (descriptor == "Ljava/lang/Class;") {
+ } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.reset(GetClassRoot(kJavaLangClass));
- } else if (descriptor == "Ljava/lang/String;") {
+ } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.reset(GetClassRoot(kJavaLangString));
- } else if (descriptor == "Ljava/lang/DexCache;") {
+ } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.reset(GetClassRoot(kJavaLangDexCache));
- } else if (descriptor == "Ljava/lang/reflect/ArtField;") {
+ } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
klass.reset(GetClassRoot(kJavaLangReflectArtField));
- } else if (descriptor == "Ljava/lang/reflect/ArtMethod;") {
+ } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.reset(GetClassRoot(kJavaLangReflectArtMethod));
} else {
klass.reset(AllocClass(self, SizeOfClass(dex_file, dex_class_def)));
@@ -1414,32 +1392,33 @@
LoadClass(dex_file, dex_class_def, klass, class_loader);
// Check for a pending exception during load
if (self->IsExceptionPending()) {
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return NULL;
}
ObjectLock lock(self, klass.get());
klass->SetClinitThreadId(self->GetTid());
- // Add the newly loaded class to the loaded classes table.
- SirtRef<mirror::Class> existing(self, InsertClass(descriptor, klass.get(), false));
- if (existing.get() != NULL) {
- // We failed to insert because we raced with another thread.
- return EnsureResolved(self, existing.get());
+ {
+ // Add the newly loaded class to the loaded classes table.
+ mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor));
+ if (existing != NULL) {
+ // We failed to insert because we raced with another thread. Calling EnsureResolved may cause
+ // this thread to block.
+ return EnsureResolved(self, existing);
+ }
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
- klass->SetStatus(mirror::Class::kStatusError);
- lock.NotifyAll();
+ klass->SetStatus(mirror::Class::kStatusError, self);
return NULL;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
CHECK(!klass->IsResolved());
- if (!LinkClass(klass, NULL)) {
+ if (!LinkClass(klass, NULL, self)) {
// Linking failed.
- klass->SetStatus(mirror::Class::kStatusError);
- lock.NotifyAll();
+ klass->SetStatus(mirror::Class::kStatusError, self);
return NULL;
}
CHECK(klass->IsResolved());
@@ -1734,7 +1713,7 @@
klass->SetAccessFlags(access_flags);
klass->SetClassLoader(class_loader);
DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
- klass->SetStatus(mirror::Class::kStatusIdx);
+ klass->SetStatus(mirror::Class::kStatusIdx, NULL);
klass->SetDexTypeIndex(dex_class_def.class_idx_);
@@ -1966,11 +1945,13 @@
mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type) {
CHECK(primitive_class != NULL);
// Must hold lock on object when initializing.
- ObjectLock lock(Thread::Current(), primitive_class);
+ Thread* self = Thread::Current();
+ ObjectLock lock(self, primitive_class);
primitive_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
primitive_class->SetPrimitiveType(type);
- primitive_class->SetStatus(mirror::Class::kStatusInitialized);
- mirror::Class* existing = InsertClass(Primitive::Descriptor(type), primitive_class, false);
+ primitive_class->SetStatus(mirror::Class::kStatusInitialized, self);
+ const char* descriptor = Primitive::Descriptor(type);
+ mirror::Class* existing = InsertClass(descriptor, primitive_class, Hash(descriptor));
CHECK(existing == NULL) << "InitPrimitiveClass(" << type << ") failed";
return primitive_class;
}
@@ -1988,12 +1969,11 @@
// array class; that always comes from the base element class.
//
// Returns NULL with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(const std::string& descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor,
mirror::ClassLoader* class_loader) {
- CHECK_EQ('[', descriptor[0]);
-
// Identify the underlying component type
- mirror::Class* component_type = FindClass(descriptor.substr(1).c_str(), class_loader);
+ CHECK_EQ('[', descriptor[0]);
+ mirror::Class* component_type = FindClass(descriptor + 1, class_loader);
if (component_type == NULL) {
DCHECK(Thread::Current()->IsExceptionPending());
return NULL;
@@ -2017,7 +1997,7 @@
// class to the hash table --- necessary because of possible races with
// other threads.)
if (class_loader != component_type->GetClassLoader()) {
- mirror::Class* new_class = LookupClass(descriptor.c_str(), component_type->GetClassLoader());
+ mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
if (new_class != NULL) {
return new_class;
}
@@ -2033,21 +2013,23 @@
// link step.
Thread* self = Thread::Current();
SirtRef<mirror::Class> new_class(self, NULL);
- if (!init_done_) {
+ if (UNLIKELY(!init_done_)) {
// Classes that were hand created, ie not by FindSystemClass
- if (descriptor == "[Ljava/lang/Class;") {
+ if (strcmp(descriptor, "[Ljava/lang/Class;") == 0) {
new_class.reset(GetClassRoot(kClassArrayClass));
- } else if (descriptor == "[Ljava/lang/Object;") {
+ } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) {
new_class.reset(GetClassRoot(kObjectArrayClass));
- } else if (descriptor == class_roots_descriptors_[kJavaLangStringArrayClass]) {
+ } else if (strcmp(descriptor, class_roots_descriptors_[kJavaLangStringArrayClass]) == 0) {
new_class.reset(GetClassRoot(kJavaLangStringArrayClass));
- } else if (descriptor == class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) {
+ } else if (strcmp(descriptor,
+ class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) == 0) {
new_class.reset(GetClassRoot(kJavaLangReflectArtMethodArrayClass));
- } else if (descriptor == class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) {
+ } else if (strcmp(descriptor,
+ class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) == 0) {
new_class.reset(GetClassRoot(kJavaLangReflectArtFieldArrayClass));
- } else if (descriptor == "[C") {
+ } else if (strcmp(descriptor, "[C") == 0) {
new_class.reset(GetClassRoot(kCharArrayClass));
- } else if (descriptor == "[I") {
+ } else if (strcmp(descriptor, "[I") == 0) {
new_class.reset(GetClassRoot(kIntArrayClass));
}
}
@@ -2065,7 +2047,7 @@
new_class->SetVTable(java_lang_Object->GetVTable());
new_class->SetPrimitiveType(Primitive::kPrimNot);
new_class->SetClassLoader(component_type->GetClassLoader());
- new_class->SetStatus(mirror::Class::kStatusInitialized);
+ new_class->SetStatus(mirror::Class::kStatusInitialized, self);
// don't need to set new_class->SetObjectSize(..)
// because Object::SizeOf delegates to Array::SizeOf
@@ -2096,7 +2078,7 @@
new_class->SetAccessFlags(access_flags);
- mirror::Class* existing = InsertClass(descriptor, new_class.get(), false);
+ mirror::Class* existing = InsertClass(descriptor, new_class.get(), Hash(descriptor));
if (existing == NULL) {
return new_class.get();
}
@@ -2137,8 +2119,8 @@
return NULL;
}
-mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::Class* klass,
- bool image_class) {
+mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass,
+ size_t hash) {
if (VLOG_IS_ON(class_linker)) {
mirror::DexCache* dex_cache = klass->GetDexCache();
std::string source;
@@ -2148,21 +2130,22 @@
}
LOG(INFO) << "Loaded class " << descriptor << source;
}
- size_t hash = StringPieceHash()(descriptor);
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- Table& classes = image_class ? image_classes_ : classes_;
mirror::Class* existing =
- LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, classes);
-#ifndef NDEBUG
- // Check we don't have the class in the other table in error
- Table& other_classes = image_class ? classes_ : image_classes_;
- CHECK(LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, other_classes) == NULL);
-#endif
+ LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
if (existing != NULL) {
return existing;
}
+ if (kIsDebugBuild && klass->GetClassLoader() == NULL && dex_cache_image_class_lookup_required_) {
+ // Check a class loaded with the system class loader matches one in the image if the class
+ // is in the image.
+ existing = LookupClassFromImage(descriptor);
+ if (existing != NULL) {
+ CHECK(klass == existing);
+ }
+ }
Runtime::Current()->GetHeap()->VerifyObject(klass);
- classes.insert(std::make_pair(hash, klass));
+ class_table_.insert(std::make_pair(hash, klass));
Dirty();
return NULL;
}
@@ -2170,23 +2153,13 @@
bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) {
size_t hash = Hash(descriptor);
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- // TODO: determine if its better to search classes_ or image_classes_ first
ClassHelper kh;
- for (auto it = classes_.lower_bound(hash), end = classes_.end(); it != end && it->first == hash;
+ for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash;
++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
- classes_.erase(it);
- return true;
- }
- }
- for (auto it = image_classes_.lower_bound(hash), end = classes_.end();
- it != end && it->first == hash; ++it) {
- mirror::Class* klass = it->second;
- kh.ChangeClass(klass);
- if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
- image_classes_.erase(it);
+ class_table_.erase(it);
return true;
}
}
@@ -2196,64 +2169,153 @@
mirror::Class* ClassLinker::LookupClass(const char* descriptor,
const mirror::ClassLoader* class_loader) {
size_t hash = Hash(descriptor);
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- // TODO: determine if its better to search classes_ or image_classes_ first
- mirror::Class* klass = NULL;
- // Use image class only if the class_loader is null.
- if (class_loader == NULL) {
- klass = LookupClassLocked(descriptor, class_loader, hash, image_classes_);
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
+ if (result != NULL) {
+ return result;
+ }
}
- if (klass != NULL) {
- return klass;
+ if (class_loader != NULL || !dex_cache_image_class_lookup_required_) {
+ return NULL;
+ } else {
+ // Lookup failed but need to search dex_caches_.
+ mirror::Class* result = LookupClassFromImage(descriptor);
+ if (result != NULL) {
+ InsertClass(descriptor, result, hash);
+ } else {
+ // Searching the image dex files/caches failed, we don't want to get into this situation
+ // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
+ // classes into the class table.
+ const int32_t kMaxFailedDexCacheLookups = 1000;
+ if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
+ MoveImageClassesToClassTable();
+ }
+ }
+ return result;
}
- return LookupClassLocked(descriptor, class_loader, hash, classes_);
}
-mirror::Class* ClassLinker::LookupClassLocked(const char* descriptor,
- const mirror::ClassLoader* class_loader,
- size_t hash, const Table& classes) {
+mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
+ const mirror::ClassLoader* class_loader,
+ size_t hash) {
ClassHelper kh(NULL, this);
- auto end = classes_.end();
- for (auto it = classes.lower_bound(hash); it != end && it->first == hash;
- ++it) {
+ auto end = class_table_.end();
+ for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
- if (strcmp(descriptor, kh.GetDescriptor()) == 0 && klass->GetClassLoader() == class_loader) {
-#ifndef NDEBUG
- for (++it; it != end && it->first == hash; ++it) {
- mirror::Class* klass2 = it->second;
- kh.ChangeClass(klass2);
- CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
- << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
- << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+ if (klass->GetClassLoader() == class_loader && strcmp(descriptor, kh.GetDescriptor()) == 0) {
+ if (kIsDebugBuild) {
+ // Check for duplicates in the table.
+ for (++it; it != end && it->first == hash; ++it) {
+ mirror::Class* klass2 = it->second;
+ kh.ChangeClass(klass2);
+ CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
+ << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
+ << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+ }
}
-#endif
return klass;
}
}
return NULL;
}
-void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& classes) {
- classes.clear();
- size_t hash = Hash(descriptor);
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- // TODO: determine if its better to search classes_ or image_classes_ first
+static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
+ CHECK(image != NULL);
+ mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ return root->AsObjectArray<mirror::DexCache>();
+}
+
+void ClassLinker::MoveImageClassesToClassTable() {
+ Thread* self = Thread::Current();
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ if (!dex_cache_image_class_lookup_required_) {
+ return; // All dex cache classes are already in the class table.
+ }
+ const char* old_no_suspend_cause =
+ self->StartAssertNoThreadSuspension("Moving image classes to class table");
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
ClassHelper kh(NULL, this);
- for (auto it = classes_.lower_bound(hash), end = classes_.end(); it != end && it->first == hash;
- ++it) {
- mirror::Class* klass = it->second;
- kh.ChangeClass(klass);
- if (strcmp(descriptor, kh.GetDescriptor()) == 0) {
- classes.push_back(klass);
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0; j < types->GetLength(); j++) {
+ mirror::Class* klass = types->Get(j);
+ if (klass != NULL) {
+ kh.ChangeClass(klass);
+ DCHECK(klass->GetClassLoader() == NULL);
+ const char* descriptor = kh.GetDescriptor();
+ size_t hash = Hash(descriptor);
+ mirror::Class* existing = LookupClassFromTableLocked(descriptor, NULL, hash);
+ if (existing != NULL) {
+ CHECK(existing == klass) << PrettyClassAndClassLoader(existing) << " != "
+ << PrettyClassAndClassLoader(klass);
+ } else {
+ class_table_.insert(std::make_pair(hash, klass));
+ }
+ }
}
}
- for (auto it = image_classes_.lower_bound(hash), end = classes_.end();
+ Dirty();
+ dex_cache_image_class_lookup_required_ = false;
+ self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+}
+
+mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
+ Thread* self = Thread::Current();
+ const char* old_no_suspend_cause =
+ self->StartAssertNoThreadSuspension("Image class lookup");
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ // First search using the class def map, but don't bother for non-class types.
+ if (descriptor[0] == 'L') {
+ const DexFile::ClassDef* class_def = dex_file->FindClassDef(descriptor);
+ if (class_def != NULL) {
+ mirror::Class* klass = dex_cache->GetResolvedType(class_def->class_idx_);
+ if (klass != NULL) {
+ self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+ return klass;
+ }
+ }
+ }
+ // Now try binary searching the string/type index.
+ const DexFile::StringId* string_id = dex_file->FindStringId(descriptor);
+ if (string_id != NULL) {
+ const DexFile::TypeId* type_id =
+ dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
+ if (type_id != NULL) {
+ uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+ mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+ if (klass != NULL) {
+ self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+ return klass;
+ }
+ }
+ }
+ }
+ self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+ return NULL;
+}
+
+void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) {
+ result.clear();
+ if (dex_cache_image_class_lookup_required_) {
+ MoveImageClassesToClassTable();
+ }
+ size_t hash = Hash(descriptor);
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ClassHelper kh(NULL, this);
+ for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
it != end && it->first == hash; ++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
if (strcmp(descriptor, kh.GetDescriptor()) == 0) {
- classes.push_back(klass);
+ result.push_back(klass);
}
}
}
@@ -2277,12 +2339,12 @@
}
if (klass->GetStatus() == mirror::Class::kStatusResolved) {
- klass->SetStatus(mirror::Class::kStatusVerifying);
+ klass->SetStatus(mirror::Class::kStatusVerifying, self);
} else {
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime)
<< PrettyClass(klass);
CHECK(!Runtime::Current()->IsCompiler());
- klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime);
+ klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime, self);
}
// Verify super class.
@@ -2307,7 +2369,7 @@
if (cause.get() != NULL) {
self->GetException(NULL)->SetCause(cause.get());
}
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return;
}
}
@@ -2322,7 +2384,7 @@
<< klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification",
PrettyDescriptor(klass).c_str());
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return;
}
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -2344,10 +2406,10 @@
// Even though there were no verifier failures we need to respect whether the super-class
// was verified or requiring runtime reverification.
if (super.get() == NULL || super->IsVerified()) {
- klass->SetStatus(mirror::Class::kStatusVerified);
+ klass->SetStatus(mirror::Class::kStatusVerified, self);
} else {
CHECK_EQ(super->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
- klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime);
+ klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self);
// Pretend a soft failure occured so that we don't consider the class verified below.
verifier_failure = verifier::MethodVerifier::kSoftFailure;
}
@@ -2357,9 +2419,9 @@
// failures at runtime will be handled by slow paths in the generated
// code. Set status accordingly.
if (Runtime::Current()->IsCompiler()) {
- klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime);
+ klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self);
} else {
- klass->SetStatus(mirror::Class::kStatusVerified);
+ klass->SetStatus(mirror::Class::kStatusVerified, self);
}
}
} else {
@@ -2368,7 +2430,7 @@
<< " because: " << error_msg;
self->AssertNoPendingException();
ThrowVerifyError(klass, "%s", error_msg.c_str());
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
}
if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Class is verified so we don't need to do any access check on its methods.
@@ -2522,7 +2584,7 @@
mirror::Class* proxy_class = GetClassRoot(kJavaLangReflectProxy);
klass->SetDexCache(proxy_class->GetDexCache());
- klass->SetStatus(mirror::Class::kStatusIdx);
+ klass->SetStatus(mirror::Class::kStatusIdx, self);
klass->SetDexTypeIndex(DexFile::kDexNoIndex16);
@@ -2555,19 +2617,20 @@
}
klass->SetSuperClass(proxy_class); // The super class is java.lang.reflect.Proxy
- klass->SetStatus(mirror::Class::kStatusLoaded); // Class is now effectively in the loaded state
+ klass->SetStatus(mirror::Class::kStatusLoaded, self); // Class is now effectively in the loaded state
self->AssertNoPendingException();
- // Link the fields and virtual methods, creating vtable and iftables
- if (!LinkClass(klass, interfaces)) {
- klass->SetStatus(mirror::Class::kStatusError);
- return NULL;
- }
{
- ObjectLock lock(self, klass.get()); // Must hold lock on object when initializing.
+ ObjectLock lock(self, klass.get()); // Must hold lock on object when resolved.
+ // Link the fields and virtual methods, creating vtable and iftables
+ if (!LinkClass(klass, interfaces, self)) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ return NULL;
+ }
+
interfaces_sfield->SetObject(klass.get(), interfaces);
throws_sfield->SetObject(klass.get(), throws);
- klass->SetStatus(mirror::Class::kStatusInitialized);
+ klass->SetStatus(mirror::Class::kStatusInitialized, self);
}
// sanity checks
@@ -2806,7 +2869,7 @@
}
if (!ValidateSuperClassDescriptors(klass)) {
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return false;
}
@@ -2815,7 +2878,7 @@
// From here out other threads may observe that we're initializing and so changes of state
// require the a notification.
klass->SetClinitThreadId(self->GetTid());
- klass->SetStatus(mirror::Class::kStatusInitializing);
+ klass->SetStatus(mirror::Class::kStatusInitializing, self);
t0 = NanoTime();
}
@@ -2837,8 +2900,7 @@
<< (self->GetException(NULL) != NULL ? self->GetException(NULL)->Dump() : "");
ObjectLock lock(self, klass);
// Initialization failed because the super-class is erroneous.
- klass->SetStatus(mirror::Class::kStatusError);
- lock.NotifyAll();
+ klass->SetStatus(mirror::Class::kStatusError, self);
return false;
}
}
@@ -2884,7 +2946,7 @@
if (self->IsExceptionPending()) {
WrapExceptionInInitializer();
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -2894,13 +2956,12 @@
global_stats->class_init_time_ns += (t1 - t0);
thread_stats->class_init_time_ns += (t1 - t0);
// Set the class as initialized except if failed to initialize static fields.
- klass->SetStatus(mirror::Class::kStatusInitialized);
+ klass->SetStatus(mirror::Class::kStatusInitialized, self);
if (VLOG_IS_ON(class_linker)) {
ClassHelper kh(klass);
LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
}
}
- lock.NotifyAll();
}
return success;
}
@@ -2917,7 +2978,7 @@
// "interruptShouldThrow" was set), bail out.
if (self->IsExceptionPending()) {
WrapExceptionInInitializer();
- klass->SetStatus(mirror::Class::kStatusError);
+ klass->SetStatus(mirror::Class::kStatusError, self);
return false;
}
// Spurious wakeup? Go back to waiting.
@@ -3061,7 +3122,7 @@
}
bool ClassLinker::LinkClass(SirtRef<mirror::Class>& klass,
- mirror::ObjectArray<mirror::Class>* interfaces) {
+ mirror::ObjectArray<mirror::Class>* interfaces, Thread* self) {
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
if (!LinkSuperClass(klass)) {
return false;
@@ -3078,7 +3139,7 @@
CreateReferenceInstanceOffsets(klass);
CreateReferenceStaticOffsets(klass);
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
- klass->SetStatus(mirror::Class::kStatusResolved);
+ klass->SetStatus(mirror::Class::kStatusResolved, self);
return true;
}
@@ -3123,7 +3184,7 @@
}
}
// Mark the class as loaded.
- klass->SetStatus(mirror::Class::kStatusLoaded);
+ klass->SetStatus(mirror::Class::kStatusLoaded, NULL);
return true;
}
@@ -3175,13 +3236,13 @@
return false;
}
-#ifndef NDEBUG
- // Ensure super classes are fully resolved prior to resolving fields..
- while (super != NULL) {
- CHECK(super->IsResolved());
- super = super->GetSuperClass();
+ if (kIsDebugBuild) {
+ // Ensure super classes are fully resolved prior to resolving fields..
+ while (super != NULL) {
+ CHECK(super->IsResolved());
+ super = super->GetSuperClass();
+ }
}
-#endif
return true;
}
@@ -3992,16 +4053,16 @@
return dex_file.GetMethodShorty(method_id, length);
}
-void ClassLinker::DumpAllClasses(int flags) const {
+void ClassLinker::DumpAllClasses(int flags) {
+ if (dex_cache_image_class_lookup_required_) {
+ MoveImageClassesToClassTable();
+ }
// TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker
// lock held, because it might need to resolve a field's type, which would try to take the lock.
std::vector<mirror::Class*> all_classes;
{
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (const std::pair<size_t, mirror::Class*>& it : classes_) {
- all_classes.push_back(it.second);
- }
- for (const std::pair<size_t, mirror::Class*>& it : image_classes_) {
+ for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
all_classes.push_back(it.second);
}
}
@@ -4011,15 +4072,20 @@
}
}
-void ClassLinker::DumpForSigQuit(std::ostream& os) const {
+void ClassLinker::DumpForSigQuit(std::ostream& os) {
+ if (dex_cache_image_class_lookup_required_) {
+ MoveImageClassesToClassTable();
+ }
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- os << "Loaded classes: " << image_classes_.size() << " image classes; "
- << classes_.size() << " allocated classes\n";
+ os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
}
-size_t ClassLinker::NumLoadedClasses() const {
+size_t ClassLinker::NumLoadedClasses() {
+ if (dex_cache_image_class_lookup_required_) {
+ MoveImageClassesToClassTable();
+ }
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- return classes_.size() + image_classes_.size();
+ return class_table_.size();
}
pid_t ClassLinker::GetClassesLockOwner() {