Add fast path to VMClassLoader.findLoadedClass
VMClassLoader.findLoadedClass now calls FindClassInPathClassLoader
as a fast path. Exclusive time results (trace view maps launch):
Before:
nativeFillInStackTrace 1.4%
defineClassNative 1.2%
findLoadedClass 0.2%
After:
nativeFillInStackTrace 0.5%
defineClassNative 0.0%
findLoadedClass 0.9%
(cherry picked from commit 194116c836080de14245a3a7c4617d07b8abf8cf)
Change-Id: I63fd7b4bccb71789e92bd39d1d3f9d0de22535de
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f94535c..16cddd5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2071,6 +2071,91 @@
reinterpret_cast<const DexFile::ClassDef*>(NULL));
}
+mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self, const char* descriptor,
+ ConstHandle<mirror::ClassLoader> class_loader) {
+ if (class_loader->GetClass() !=
+ soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
+ class_loader->GetParent()->GetClass() !=
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
+ return nullptr;
+ }
+ ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ // Check if this would be found in the parent boot class loader.
+ if (pair.second != nullptr) {
+ mirror::Class* klass = LookupClass(descriptor, nullptr);
+ if (klass != nullptr) {
+ return EnsureResolved(self, descriptor, klass);
+ }
+ klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
+ if (klass != nullptr) {
+ return klass;
+ }
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ } else {
+ // RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<3> hs(self);
+ // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+ // We need to get the DexPathList and loop through it.
+ Handle<mirror::ArtField> cookie_field =
+ hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
+ Handle<mirror::ArtField> dex_file_field =
+ hs.NewHandle(
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+ mirror::Object* dex_path_list =
+ soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ GetObject(class_loader.Get());
+ if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
+ cookie_field.Get() != nullptr) {
+ // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+ mirror::Object* dex_elements_obj =
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ GetObject(dex_path_list);
+ // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+ // at the mCookie which is a DexFile vector.
+ if (dex_elements_obj != nullptr) {
+ Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+ hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+ for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+ mirror::Object* element = dex_elements->GetWithoutChecks(i);
+ if (element == nullptr) {
+ // Should never happen, fall back to java code to throw a NPE.
+ break;
+ }
+ mirror::Object* dex_file = dex_file_field->GetObject(element);
+ if (dex_file != nullptr) {
+ const uint64_t cookie = cookie_field->GetLong(dex_file);
+ auto* dex_files =
+ reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
+ if (dex_files == nullptr) {
+ // This should never happen so log a warning.
+ LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
+ break;
+ }
+ for (const DexFile* dex_file : *dex_files) {
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ if (dex_class_def != nullptr) {
+ RegisterDexFile(*dex_file);
+ mirror::Class* klass =
+ DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ return nullptr;
+ }
+ return klass;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
ConstHandle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
@@ -2114,7 +2199,6 @@
// a NoClassDefFoundError being allocated.
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
if (pair.second != nullptr) {
- StackHandleScope<1> hs(self);
return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
}
// Next try the compile time class path.
@@ -2131,86 +2215,9 @@
}
} else {
ScopedObjectAccessUnchecked soa(self);
- if (class_loader->GetClass() ==
- soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) &&
- class_loader->GetParent()->GetClass() ==
- soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
- ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
- // Check if this would be found in the parent boot class loader.
- if (pair.second != nullptr) {
- mirror::Class* klass = LookupClass(descriptor, nullptr);
- if (klass != nullptr) {
- return EnsureResolved(self, descriptor, klass);
- }
- klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
- *pair.second);
- if (klass == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- } else {
- return klass;
- }
- } else {
- // RegisterDexFile may allocate dex caches (and cause thread suspension).
- StackHandleScope<3> hs(self);
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
- Handle<mirror::ArtField> cookie_field =
- hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
- Handle<mirror::ArtField> dex_file_field =
- hs.NewHandle(
- soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
- mirror::Object* dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
- GetObject(class_loader.Get());
- if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
- cookie_field.Get() != nullptr) {
- // DexPathList has an array dexElements of Elements[] which each contain a dex file.
- mirror::Object* dex_elements_obj =
- soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
- // at the mCookie which is a DexFile vector.
- if (dex_elements_obj != nullptr) {
- Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
- hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
- for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
- mirror::Object* element = dex_elements->GetWithoutChecks(i);
- if (element == nullptr) {
- // Should never happen, fall back to java code to throw a NPE.
- break;
- }
- mirror::Object* dex_file = dex_file_field->GetObject(element);
- if (dex_file != nullptr) {
- const uint64_t cookie = cookie_field->GetLong(dex_file);
- auto* dex_files =
- reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
- if (dex_files == nullptr) {
- // This should never happen so log a warning.
- LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
- break;
- }
- for (const DexFile* dex_file : *dex_files) {
- const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
- if (dex_class_def != nullptr) {
- RegisterDexFile(*dex_file);
- mirror::Class* klass =
- DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
- if (klass == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- // Exit the loop to make the java code generate an exception.
- i = dex_elements->GetLength();
- break;
- }
- return klass;
- }
- }
- }
- }
- }
- }
- }
+ mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, class_loader);
+ if (klass != nullptr) {
+ return klass;
}
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7afa6a1..11ac326 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -76,6 +76,12 @@
ConstHandle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Find a class in the path class loader, loading it if necessary.
+ mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self, const char* descriptor,
+ ConstHandle<mirror::ClassLoader> class_loader)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Finds a class by its descriptor using the "system" class loader, ie by searching the
// boot_class_path_.
mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index fefddae..761e800 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -31,16 +31,23 @@
if (name.c_str() == NULL) {
return NULL;
}
-
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
std::string descriptor(DotToDescriptor(name.c_str()));
- mirror::Class* c = Runtime::Current()->GetClassLinker()->LookupClass(descriptor.c_str(), loader);
+ mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader);
if (c != NULL && c->IsResolved()) {
return soa.AddLocalReference<jclass>(c);
- } else {
- // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
- // the regular loadClass code.
- return NULL;
}
+ if (loader != nullptr) {
+ // Try the common case.
+ StackHandleScope<1> hs(soa.Self());
+ c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), hs.NewHandle(loader));
+ if (c != nullptr) {
+ return soa.AddLocalReference<jclass>(c);
+ }
+ }
+ // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
+ // the regular loadClass code.
+ return NULL;
}
static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {