先看看ART中跟类的查找关系最密切的classlinker的初始化过程。当启动的时候存在boot.art的时候,将从imagespace中初始化classlinker;当没有boot.art的时候,将脱离boot.art调用InitWithoutImage初始化classlinker。由于dex2oat进程生成boot.art时没有image,这时候调用的是InitWithoutImage来加载基本类,生成对应的DexCache。
/art/runtime/runtime.cc
if (GetHeap()->HasImageSpace()) {
class_linker_->InitFromImage();
if (kIsDebugBuild) {
GetHeap()->GetImageSpace()->VerifyImageAllocations();
}
}
产生boot.art所用的art虚拟机和系统启动的art虚拟机不是同一个虚拟机,因为产生boot.art使用的是dex2oat。系统启动时的classlinker初始化所需要的DexCache(Dex文件被加载到内存的表示)和class_roots_(一些基本类的集合)都可以直接从boot.art中获得,因为dex2oat生成boot.art时将这些基本类和DexCache的地址都记录好了。
通过关联类名和class_roots_的类,我们可以通过直接使用类名加载类所在的内存达到使用的目的;相似地,我们可以通过直接使用DexFile加载对应的DexCache来达到使用目的,因为在boot_class_path_保存了对应于image里面的DexCache的DexFile。
/art/runtime/class_linker.cc
void ClassLinker::InitFromImage() {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
Thread* self = Thread::Current();
gc::Heap* heap = Runtime::Current()->GetHeap();
gc::space::ImageSpace* space = heap->GetImageSpace();
dex_cache_image_class_lookup_required_ = true;
CHECK(space != nullptr);
OatFile& oat_file = GetImageOatFile(space);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
const char* image_file_location = oat_file.GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline();
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
//关联classlinker加载过的dex文件
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
StackHandleScope<1> hs(self);
//关联一些基本类型的类
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// Special case of setting up the String class early so that we can test arbitrary objects
// as being Strings or not
//将String与基本类kJavaLangString关联
mirror::String::SetClass(GetClassRoot(kJavaLangString));
//理论上boot.oat中的DexFile数量和boot.art注册的DexCache数量相同
CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
StackHandleScope<1> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_caches->Get(i)));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
//OatDexFile是DexFile在OatFile中的表示
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
nullptr);
CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
std::string error_msg;
//找到对应的DexFile
const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
<< " from within oat file " << oat_file.GetLocation()
<< " error '" << error_msg << "'";
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
//添加DexFile到boot_class_path_中,并且注册DexCache
AppendToBootClassPath(*dex_file, dex_cache);
}
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
// bitmap walk.
//关联ArtMethod和kJavaLangReflectArtMethod mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize();
if (!Runtime::Current()->IsCompiler()) {
// Compiler supports having an image with a different pointer size than the runtime. This
// happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may
// also use 32 bit dex2oat on a system with 64 bit apps.
CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*)))
<< sizeof(void*);
}
if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) {
image_pointer_size_ = 4;
} else {
CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8));
image_pointer_size_ = 8;
}
// Set entry point to interpreter if in InterpretOnly mode.
if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
heap->VisitObjects(InitFromImageInterpretOnlyCallback, this);
}
// reinit class_roots_
//关联Class和kJavaLangClass
mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// reinit array_iftable_ from any array class instance, they should be ==
//关联。。。
array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable());
DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable());
// String class root was set above
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass));
mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
//检查步骤
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromImage exiting";
}
对于类的查找加载,我们可以从Class的ForName方法谈起。
VMStack.getCallingClassLoader()返回的是当前线程的classloader。
/libcore/libart/src/main/java/java/lang/Class.java
public static Class forName(String className) throws ClassNotFoundException {
return forName(className, true, VMStack.getCallingClassLoader());
}
如果传入的classloader为空,则设置classloader为parent是BootClassLoader的PathClassLoader。接着调用native方法classForName。
/libcore/libart/src/main/java/java/lang/Class.java
public static Class forName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
// Catch an Exception thrown by the underlying native code. It wraps
// up everything inside a ClassNotFoundException, even if e.g. an
// Error occurred during initialization. This as a workaround for
// an ExceptionInInitializerError that's also wrapped. It is actually
// expected to be thrown. Maybe the same goes for other errors.
// Not wrapping up all the errors will break android though.
Class result;
try {
result = classForName(className, shouldInitialize, classLoader);
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
/art/runtime/native/java_lang_Class.cc
static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
jobject javaLoader) {
ScopedFastNativeObjectAccess soa(env);
ScopedUtfChars name(env, javaName);
if (name.c_str() == nullptr) {
return nullptr;
}
// We need to validate and convert the name (from x.y.z to x/y/z). This
// is especially handy for array types, since we want to avoid
// auto-generating bogus array classes.
if (!IsValidBinaryClassName(name.c_str())) {
ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;",
"Invalid name: %s", name.c_str());
return nullptr;
}
std::string descriptor(DotToDescriptor(name.c_str()));//将xx.xx.xx的类名转换成xx/xx/xx的形式
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> c(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
//查找类返回null会抛出ClassNotFound异常
if (c.Get() == nullptr) {
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
env->ExceptionClear();
jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
WellKnownClasses::java_lang_ClassNotFoundException_init,
javaName, cause.get()));
if (cnfe != nullptr) {
// Make sure allocation didn't fail with an OOME.
env->Throw(cnfe);
}
return nullptr;
}
//初始化父类和静态代码
if (initialize) {
class_linker->EnsureInitialized(c, true, true);
}
return soa.AddLocalReference<jclass>(c.Get());
}
/art/runtime/class_linker.cc
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
Handle class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
self->AssertNoPendingException();
//基本类型,已经被加载好在classlinker的class_roots_成员中
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
//在class_table_,pre_zygote_class_table_和image(需要classloader参数为null)中查找
mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
//数组类型,则寻找数组基本元素类型,找不到则创建新类
if (descriptor[0] == '[') {
return CreateArrayClass(self, descriptor, hash, class_loader);
} else if (class_loader.Get() == nullptr) {
没有classloader只能从boot_class_path查找,如前面所述,boot_class_path记录了image已经加载好的DexFile
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
//找到则定义一个新类
return DefineClass(self, descriptor, hash, NullHandle(), *pair.first,
*pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
// trigger the chaining with a proper stack trace.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
return nullptr;
}
} else if (Runtime::Current()->UseCompileTimeClassPath()) {//跟dex2oat相关,表示使用编译时指定的类路径
// First try with the bootstrap class loader.
if (class_loader.Get() != nullptr) {
//将classloader参数设为null,需要在image中寻找
klass = LookupClass(descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
}
// If the lookup failed search the boot class path. We don't perform a recursive call to avoid
// a NoClassDefFoundError being allocated.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle(), *pair.first,
*pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
{
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef jclass_loader(soa.Env(),
soa.AddLocalReference(class_loader.Get()));
class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
}
//在编译时指定的类路径查找
pair = FindInClassPath(descriptor, hash, *class_path);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
}
} else {
ScopedObjectAccessUnchecked soa(self);
//使用父加载器为BootClassLoader的PathClassLoader
mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
if (klass != nullptr) {
return klass;
}
ScopedLocalRef class_loader_object(soa.Env(),
soa.AddLocalReference(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
ScopedLocalRef result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
ScopedLocalRef class_name_object(soa.Env(),
soa.Env()->NewStringUTF(class_name_string.c_str()));
if (class_name_object.get() == nullptr) {
DCHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
CHECK(class_loader_object.get() != nullptr);
//调用Java层classloader的loadClass函数
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
return nullptr;
} else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
} else {
// success, return mirror::Class*
return soa.Decode(result.get());
}
}
ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
return nullptr;
}
ClassLinker::LookupClass首先根据类的描述符,由描述符产生的hash值和classloader在pre_zygote_class_table_和class_table_中查找。在Zygote fork出进程之前,会将class_table_的内容移动到pre_zygote_class_table_中,并清空class_table_,这样pre_zygote_class_table_ 记录的是zygote fork进程前加载的类,class_table_ 记录的是fork进程之后记载的类。每当调用LookupClass在image中找到类或者创建新的类时就会在class_table_中插入这个类的记录,说明这个类在近期被使用过,下次可以直接使用。
/art/runtime/class_linker.cc
mirror::Class* ClassLinker::LookupClass(const char* descriptor, size_t hash,
mirror::ClassLoader* class_loader) {
{
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
if (result != nullptr) {
return result;
}
}
if (class_loader != nullptr || !dex_cache_image_class_lookup_required_) {
//有classloader或者不需要在image文件中查找,直接返回null
return nullptr;
} else {
// Lookup failed but need to search dex_caches_.
//在image中查找
mirror::Class* result = LookupClassFromImage(descriptor);
if (result != nullptr) {
//找到clas,将其插入到class_table_中
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.
constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
//调用ClassLinker::LookupClass函数无法找到类超过1000次后,直接将image的class插入到class_table_中,下次调用此函数不需要再搜索image文件
if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
MoveImageClassesToClassTable();
}
}
return result;
}
}
/art/runtime/class_linker.cc
mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
mirror::ClassLoader* class_loader,
size_t hash) {
auto descriptor_pair = std::make_pair(descriptor, class_loader);
auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash);
if (it == pre_zygote_class_table_.end()) {
it = class_table_.FindWithHash(descriptor_pair, hash);
if (it == class_table_.end()) {
return nullptr;
}
}
return it->Read();
}
进入到FindClassInPathClassLoader函数中,首先需要检查传入的classloader对应的是否是一个父加载器为BootClassLoader的PathClassLoader,不是的话直接返回null。接着判断boot_class_path_是否包含我们需要的类,包含的话我们调用LookupClass查找类(注意这里传的classloader参数为null),找到的话会返回,找不到的就会定义一个新类(注意这里调用DefineClass传入的classloader参数为null),class有一个成员class_loader_用来标记关联的classloader,若为null,表示将会用BootClassLoader来加载这个类,BootClassLoader是所有classloader的祖宗。若boot_class_path_不包含我们需要的类,则去classloader加载好的DexFile文件里查找是否有定义这个类,若有则定义这这个新类,并在classlinker中注册这个DexFile,生成对应的DexCache。简而言之,就是优先使用BootClassLoader查找,找不到再用PathClassLoader加载好的DexFile来查找。
/art/runtime/class_linker.cc
mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self, const char* descriptor,
size_t hash,
Handle<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, hash, boot_class_path_);
// Check if this would be found in the parent boot class loader.
if (pair.second != nullptr) {
mirror::Class* klass = LookupClass(descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
klass = DefineClass(self, descriptor, hash, 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, hash);
if (dex_class_def != nullptr) {
RegisterDexFile(*dex_file);
mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file,
*dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
return nullptr;
}
return klass;
}
}
}
}
}
}
}
return nullptr;
}
最后用来查找类的方式是调用ClassLoader的loadClass方法。对于每一个classLoader加载过的类,在art内部都会有记录,所以findLoadedClass就是查找是否有加载过的记录。若没有加载记录,则使用双亲委托模型进行类的加载。如果双亲委托模式下找不到该类,则使用findClass来查找类。在自定义的classLoader中,我们可以自定义findClass来定义查找类的逻辑。双亲委托模型是指,用户自定义的classLoader首先在cache中查找被加载对象是否存在。如果存在就直接返回;否则会优先委托它的上一级classLoder来加载,上一级classLoader也委托它的上一级classLoader来加载,直到到达根classLoader即BootClassLoader为止。如果根classLoader查找不到,就按跟之前相反的顺序向下派发加载任务,直到成功加载好对象为止。
/libcore/libart/src/main/java/java/lang/ClassLoader.java
protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
再看看定义一个类的方法DefineClass的实现。定义一个类需要找到其关联的·DexFile,这个DexFile既可以从系统预加载的一系列DexFile路径boot_class_path_中得到,又可以从其ClassLoader预先打开的DexFile中找到,但是优先使用从boot_class_path_ 得到的,详情可见上面的FindClassInPathClassLoader。一般apk都是定义了自己的ClassLoader,预加载了apk包里面的DexFile,便可以在里面找到需要的类了。
/art/runtime/class_linker.cc
mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(kJavaLangObject));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(kJavaLangClass));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(kJavaLangString));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(kJavaLangRefReference));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(kJavaLangDexCache));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtField));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
}
}
//为新的类分配空间
if (klass.Get() == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
return nullptr;
}
//关联class和对应的DexCache
klass->SetDexCache(FindDexCache(dex_file));
//加载类
LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
mirror::Class* new_class = nullptr;
//链接类
if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(new_class != nullptr) << descriptor;
CHECK(new_class->IsResolved()) << descriptor;
Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));
/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
* class and initializing them to the standard default values, but not
* executing any code (that comes later, during "initialization").
*
* We did the static preparation in LinkClass.
*
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
Dbg::PostClassPrepare(new_class_h.Get());
return new_class_h.Get();
}
/art/runtime/class_linker.cc
void ClassLinker::LoadClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader) {
CHECK(klass.Get() != nullptr);
CHECK(klass->GetDexCache() != nullptr);
CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
CHECK(descriptor != nullptr);
//关联新定义的类和kJavaLangClass
klass->SetClass(GetClassRoot(kJavaLangClass));
if (kUseBakerOrBrooksReadBarrier) {
klass->AssertReadBarrierPointer();
}
//access_flags标识一个类是接口还是一个类
uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
klass->SetAccessFlags(access_flags);
//关联新定义类和入参classloader
klass->SetClassLoader(class_loader);
DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
//设置类的状态为kStatusIdx,这是进行loadClass时的状态
klass->SetStatus(mirror::Class::kStatusIdx, nullptr);
//GetIndexForClassDef得到这个类的ClassDef在DexFile的class_defs_数组字段的下标
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
//class_idx_对应这个类的TypeId在DexFile的type_ids_数组字段的下标
klass->SetDexTypeIndex(dex_class_def.class_idx_);
CHECK(klass->GetDexCacheStrings() != nullptr);
//这个class的数据字段的地址
const byte* class_data = dex_file.GetClassData(dex_class_def);
if (class_data == nullptr) {
return; // no fields or methods - for example a marker interface
}
OatFile::OatClass oat_class;
if (Runtime::Current()->IsStarted()
&& !Runtime::Current()->UseCompileTimeClassPath()
&& FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
} else {
LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
}
}
LoadClassMembers的过程就是填充刚分配好的类的内容,包括静态成员(StaticFields),普通成员(InstanceFields),private/static方法(DirectMethods),public/proteced方法(VirtualMethods),并初始化它们和class,DexFile的联系。
OAT文件包含了类的方法的本地机器指令。类的每一个方法在ART内部都会对应一个ArtMethod我们通过LinkCode来实现指定ArtMethod的本地机器指令入口。
/art/runtime/class_linker.cc
void ClassLinker::LoadClassMembers(const DexFile& dex_file,
const byte* class_data,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader,
const OatFile::OatClass* oat_class) {
// Load fields.
ClassDataItemIterator it(dex_file, class_data);
Thread* self = Thread::Current();
if (it.NumStaticFields() != 0) {
//分配静态域所需空间
mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
if (UNLIKELY(statics == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//klass的sfields_成员设定为刚分配的静态域内存
klass->SetSFields(statics);
}
if (it.NumInstanceFields() != 0) {
//分配实例域所需空间
mirror::ObjectArray<mirror::ArtField>* fields =
AllocArtFieldArray(self, it.NumInstanceFields());
if (UNLIKELY(fields == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//kclass的ifields_成员设定为刚分配的实例域内存
klass->SetIFields(fields);
}
for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(sfield.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//分别为每个静态域设置其在sfields_区域的下标
klass->SetStaticField(i, sfield.Get());
//初始化这个域在DexFile的id,设置域的声明class为kclass等
LoadField(dex_file, it, klass, sfield);
}
for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(ifield.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//分别为每个实例域设置其在ifields_区域的下标
klass->SetInstanceField(i, ifield.Get());
//初始化这个域在DexFile的id,设置域的声明class为kclass等
LoadField(dex_file, it, klass, ifield);
}
// Load methods.
if (it.NumDirectMethods() != 0) {
// TODO: append direct methods to class object
//分配直接方法(private)所需空间
mirror::ObjectArray<mirror::ArtMethod>* directs =
AllocArtMethodArray(self, it.NumDirectMethods());
if (UNLIKELY(directs == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//让klass的direct_methods_设定为刚分配的直接方法内存
klass->SetDirectMethods(directs);
}
if (it.NumVirtualMethods() != 0) {
//分配虚方法(public,protected)所需空间
// TODO: append direct methods to class object
mirror::ObjectArray<mirror::ArtMethod>* virtuals =
AllocArtMethodArray(self, it.NumVirtualMethods());
if (UNLIKELY(virtuals == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//让kclass的virtual_methods_设定为刚分配的虚方法内存
klass->SetVirtualMethods(virtuals);
}
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//分别为每个直接方法设置其在direct_methods_区域的下标
klass->SetDirectMethod(i, method.Get());
//初始化每个直接方法的入口
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if (last_dex_method_index == it_method_index) {
// duplicate case
method->SetMethodIndex(last_class_def_method_index);
} else {
method->SetMethodIndex(class_def_method_index);
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
//分别为每个虚方法设置其在virtual_methods_区域的下标
klass->SetVirtualMethod(i, method.Get());
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
//初始化每个虚方法的入口
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
class_def_method_index++;
}
DCHECK(!it.HasNext());
}
ART运行时将DEX字节码翻译成本地机器指令时,使用的后端有两种:quick和portable。art使用的默认时quick,下面的内容将以后端为quick为前提展开。
若在oat文件里找到对应的oat_class,直接将ArtMethod的入口设置为
对应的本地机器指令偏移处。但不是所有的java代码都在对应的oat文件里有对应的本地机器指令,当有本地机器指令时,对应的本地机器指令偏移就是一个不为0的值,没有本地机器指令时,对应的本地机器指令偏移就是0。后面会提到,对应的本地机器指令偏移是0的情况下,就需要通过解释器模式执行了。
ART兼容了dalvik时代的解释器模式,允许类方法直接使用解释器模式运行,但是这种情况多发生在虚拟机尚未启动时。
SetEntryPointFromInterpreter用来设置解释器运行的入口的。如果这个方法仍需要用解释器运行且不为jni方法,我们需要一个桥接方法来运行,顾名思义就是artInterpreterToInterpreterBridge,意思是设置一个还是走解释器模式执行的解释器入口函数,最终会走到一个汇编函数里面进行跳转。否则,我们需要从解释器桥接到本地机器指令,即artInterpreterToCompiledCodeBridge,意思是设置一个改走本地机器指令执行的解释器入口函数。
若此方法为抽象方法,由于没有本地机器指令,于是我们假装有本地机器指令,使用SetEntryPointFromQuickCompiledCode从这个本地机器指令处跳转到解释器模式执行,就是artInterpreterToInterpreterBridge,最终会抛出异常。我们可以使用”oatdump –oat-file=xxx”来确认一个oat文件的方法是否有本地机器指令。
如果方法是静态方法且不是构造器,则使用延迟链接技术。静态非构造器方法的本地机器指令入口点为GetQuickResolutionTrampoline()返回的一个蹦床函数art_quick_resolution_trampoline,是由汇编写成的一个平台相关的函数,这个函数会先跳转到artQuickResolutionTrampoline函数,之后在这个函数中完成解析,保存本地机器指令入口到r12寄存器中,再使用bx指令跳转过去执行本地机器指令。
如果是需要进入解释器模式运行的普通方法或构造器,对于非jni方法,使用GetQuickToInterpreterBridge从本地机器指令桥接到解释器模式,所以说没有本地机器指令的java普通方法会走到这个蹦床函数,从而进入解释器模式。对于jni方法,使用GetQuickGenericJniTrampoline从本地机器指令桥接到jni蹦床函数。
否则,如果该方法拥有portable后端编译出来的本地机器指令,设置入口为该机器指令;没有portable后端编译出来的本地机器指令的情况则使用quick后端编译出来的机器指令作为入口。
/art/runtime/class_linker.cc
void ClassLinker::LinkCode(Handle method, const OatFile::OatClass* oat_class,
const DexFile& dex_file, uint32_t dex_method_index,
uint32_t method_index) {
if (Runtime::Current()->IsCompiler()) {
// The following code only applies to a non-compiler runtime.
//虚拟机参数指定了"compilercallbacks"时直接返回
return;
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
#endif
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
//在oat文件已经包含这个类的情况下,设置这个ArtMethod的本地机器指令入口为OatMethod的code_offset_
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
oat_method.LinkMethod(method.Get());
}
// Install entry point from interpreter.
//解释器模式执行类方法。发生在:1.没有生成对应的本地机器指令;2.指定解释器方式执行且此方法不是JNI方法和代理方法
bool enter_interpreter = NeedsInterpreter(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode());
#else
nullptr);
#endif
//这里设置解释器入口点
if (enter_interpreter && !method->IsNative()) {
//解释器模式执行且不是JNI方法
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
} else {
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
//抽象方法,抽象方法由子类实现,没有对应的本地机器指令,这个时候应该将GetQuickToInterpreterBridge返回的函数指针作为该方法的入口
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
return;
}
bool have_portable_code = false;
//静态方法且不是构造方法,使用GetQuickResolutionTrampoline返回的函数指针作为该方法的入口
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
#endif
} else if (enter_interpreter) {//解释器模式
if (!method->IsNative()) {//非JNI方法
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
}
#if defined(ART_USE_PORTABLE_COMPILER)
} else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {//方法由portable后端生成的本地机器指令
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
have_portable_code = true;
method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
#endif
} else {
DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
}
if (method->IsNative()) {//方法为JNI方法
// Unregistering restores the dlsym lookup stub.
//注册寻找对应的Native方法的函数art_jni_dlsym_lookup_stub
method->UnregisterNative(Thread::Current());
if (enter_interpreter) {
// We have a native method here without code. Then it should have either the GenericJni
// trampoline as entrypoint (non-static), or the Resolution trampoline (static).
DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline()
|| method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline());
}
}
// Allow instrumentation its chance to hijack code.
Runtime* runtime = Runtime::Current();
runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode(),
#else
nullptr,
#endif
have_portable_code);
}
之前提到,每个加载过的DexFile都会关联一个DexCache,DexCache缓存了DexFile的指针,String字符串,类,方法,域,位置等信息。AllocDexCache首先调用AllocObject为DexCache分配了空间,其次为String字符串,类,方法,域分配空间,最后调用DexCache::Init完成DexCache的初始化。
/art/runtime/class_linker.cc
mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
gc::Heap* heap = Runtime::Current()->GetHeap();
StackHandleScope<16> hs(self);
Handle<mirror::Class> dex_cache_class(hs.NewHandle(GetClassRoot(kJavaLangDexCache)));
Handle<mirror::DexCache> dex_cache(
hs.NewHandle(down_cast<mirror::DexCache*>(
heap->AllocObject<true>(self, dex_cache_class.Get(), dex_cache_class->GetObjectSize(),
VoidFunctor()))));
if (dex_cache.Get() == nullptr) {
return nullptr;
}
Handle<mirror::String>
location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
if (location.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::String>>
strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
if (strings.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::Class>>
types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
if (types.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::ArtMethod>>
methods(hs.NewHandle(AllocArtMethodArray(self, dex_file.NumMethodIds())));
if (methods.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::ArtField>>
fields(hs.NewHandle(AllocArtFieldArray(self, dex_file.NumFieldIds())));
if (fields.Get() == nullptr) {
return nullptr;
}
dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
fields.Get());
return dex_cache.Get();
}
开始DexCache的strings_,resolved_fields_,resolved_types_均由一个刚分配好空间的String数组,ArtField数组和Class数组填充,真正的内容在解析类的时候才会添加上去。runtime->GetResolutionMethod()返回的实际是上面提到的art_quick_resolution_trampoline表示的蹦床函数,在实际解析完之后也会添加真正的方法本地机器指令入口。
/art/runtime/mirror/dex_cache.cc
void DexCache::Init(const DexFile* dex_file,
String* location,
ObjectArray<String>* strings,
ObjectArray<Class>* resolved_types,
ObjectArray<ArtMethod>* resolved_methods,
ObjectArray<ArtField>* resolved_fields) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
CHECK(strings != nullptr);
CHECK(resolved_types != nullptr);
CHECK(resolved_methods != nullptr);
CHECK(resolved_fields != nullptr);
SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
SetFieldObject<false>(StringsOffset(), strings);
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);
SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);
SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields);
Runtime* runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
// Initialize the resolve methods array to contain trampolines for resolution.
ArtMethod* trampoline = runtime->GetResolutionMethod();
size_t length = resolved_methods->GetLength();
for (size_t i = 0; i < length; i++) {
resolved_methods->SetWithoutChecks<false>(i, trampoline);
}
}
}
实际上,Java类方法调用另一个Java类方法的过程是通过DexCache进行的,前提是这两个Java类方法在同一个DexCaChe里面。
SetDexCacheResolvedMethods让分配出来的ArtMethod获得关联的DexFile的所有方法的入口,在首次使用这个类方法时,入口会被设置为art_quick_resolution_trampoline表示的蹦床函数,跳转以后进行真正的解析操作,然后跳到真正的执行代码中去,并使用DexCache::SetResolvedMethod设置这个入口为真正的ArtMethod指针,再次调用此方法时就不需要重新进行解析了。这样做的目的是使得类方法可以动态解析,那些不会执行到的类方法将永远不会被解析出真正的入口,从而节约了解析时间。
/art/runtime/class_linker.cc
mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file,
const ClassDataItemIterator& it,
Handle klass) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
//分配ArtMethod所需的内存
mirror::ArtMethod* dst = AllocArtMethod(self);
if (UNLIKELY(dst == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
DCHECK(dst->IsArtMethod()) << PrettyDescriptor(dst->GetClass());
const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
//SetDexCacheResolvedMethods让分配出来的ArtMethod获得关联的DexFile的所有方法的入口
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
//SetDexCacheResolvedTypes让分配出来的ArtMethod获得关联的DexFile的所有类型
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
uint32_t access_flags = it.GetMethodAccessFlags();
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
// Void return type.
if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged.
klass->SetFinalizable();
} else {
std::string temp;
const char* klass_descriptor = klass->GetDescriptor(&temp);
// The Enum class declares a "final" finalize() method to prevent subclasses from
// introducing a finalizer. We don't want to set the finalizable flag for Enum or its
// subclasses, so we exclude it here.
// We also want to avoid setting the flag on Object, where we know that finalize() is
// empty.
if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 &&
strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) {
klass->SetFinalizable();
}
}
}
} else if (method_name[0] == '<') {
// Fix broken access flags for initializers. Bug 11157540.
bool is_init = (strcmp("" , method_name) == 0);
bool is_clinit = !is_init && (strcmp("" , method_name) == 0);
if (UNLIKELY(!is_init && !is_clinit)) {
LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
} else {
if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
<< PrettyDescriptor(klass.Get()) << " in dex file " << dex_file.GetLocation();
access_flags |= kAccConstructor;
}
}
}
dst->SetAccessFlags(access_flags);
self->EndAssertNoThreadSuspension(old_cause);
return dst;
}
所以,首次调用类方法时的过程为:1.进入蹦床函数art_quick_resolution_trampoline,再跳转到C++函数artQuickResolutionTrampolin,artQuickResolutionTrampoline接受四个参数:第一个参数called表示被调用的类方法,第二个参数receiver表示被调用的对象,也就是接收消息的对象,第三个参数thread表示当前线程,第四个参数sp指向调用栈顶。2.artQuickResolutionTrampoline调用ClassLinker::ResolveMethod找到对应的ArtMethod;3.调用DexCache::SetResolvedMethod,用真正的方法覆盖蹦床函数方法,从此可以直接从DexCache找到对应的ArtMethod。
/art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
mirror::Object* receiver,
Thread* self,
StackReference* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
// Start new JNI local reference state
JNIEnvExt* env = self->GetJniEnv();
ScopedObjectAccessUnchecked soa(env);
ScopedJniEnvLocalRefState env_state(env);
const char* old_cause = self->StartAssertNoThreadSuspension("Quick method resolution set up");
// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
mirror::ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
const DexFile* dex_file;
uint32_t dex_method_idx;
if (called->IsRuntimeMethod()) {//被调用方法仍为蹦床函数
uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
const DexFile::CodeItem* code;
dex_file = caller->GetDexFile();
code = caller->GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
bool is_range;
switch (instr_code) {
case Instruction::INVOKE_DIRECT://执行直接方法即private方法
invoke_type = kDirect;
is_range = false;
break;
case Instruction::INVOKE_DIRECT_RANGE://执行参数不少于4个的直接方法
invoke_type = kDirect;
is_range = true;
break;
case Instruction::INVOKE_STATIC://执行静态方法
invoke_type = kStatic;
is_range = false;
break;
case Instruction::INVOKE_STATIC_RANGE://执行参数不少于4个的静态方法
invoke_type = kStatic;
is_range = true;
break;
case Instruction::INVOKE_SUPER://执行父类方法
invoke_type = kSuper;
is_range = false;
break;
case Instruction::INVOKE_SUPER_RANGE://执行参数不少于4个的父类方法
invoke_type = kSuper;
is_range = true;
break;
case Instruction::INVOKE_VIRTUAL://执行虚方法即public和protected方法
invoke_type = kVirtual;
is_range = false;
break;
case Instruction::INVOKE_VIRTUAL_RANGE://执行参数不少于4个的虚方法
invoke_type = kVirtual;
is_range = true;
break;
case Instruction::INVOKE_INTERFACE://执行接口方法
invoke_type = kInterface;
is_range = false;
break;
case Instruction::INVOKE_INTERFACE_RANGE://执行参数不少于4个的接口方法
invoke_type = kInterface;
is_range = true;
break;
default:
LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL);
// Avoid used uninitialized warnings.
invoke_type = kDirect;
is_range = false;
}
dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
} else {
//前面提到的静态非构造器函数走此分支
invoke_type = kStatic;
dex_file = called->GetDexFile();
dex_method_idx = called->GetDexMethodIndex();
}
uint32_t shorty_len;
const char* shorty =
dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len);
RememberForGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa);
visitor.VisitArguments();
self->EndAssertNoThreadSuspension(old_cause);
bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;
// Resolve method filling in dex cache.
if (UNLIKELY(called->IsRuntimeMethod())) {
StackHandleScope<1> hs(self);
mirror::Object* dummy = nullptr;
HandleWrapper h_receiver(
hs.NewHandleWrapper(virtual_or_interface ? &receiver : &dummy));
//ResolveMethod解析方法,得到对应的ArtMethod
called = linker->ResolveMethod(self, dex_method_idx, &caller, invoke_type);
}
const void* code = NULL;
if (LIKELY(!self->IsExceptionPending())) {
// Incompatible class change should have been handled in resolve method.
CHECK(!called->CheckIncompatibleClassChange(invoke_type))
<< PrettyMethod(called) << " " << invoke_type;
if (virtual_or_interface) {
//虚方法或接口方法
// Refine called method based on receiver.
CHECK(receiver != nullptr) << invoke_type;
mirror::ArtMethod* orig_called = called;
if (invoke_type == kVirtual) {
//虚方法在VTable(虚拟函数表)中查找
called = receiver->GetClass()->FindVirtualMethodForVirtual(called);
} else {
//接口方法在ITable(接口函数表)中查找
called = receiver->GetClass()->FindVirtualMethodForInterface(called);
}
CHECK(called != nullptr) << PrettyMethod(orig_called) << " "
<< PrettyTypeOf(receiver) << " "
<< invoke_type << " " << orig_called->GetVtableIndex();
// We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
// of the sharpened method.
//被调用方法和调用方法在同一个Dexfile的情况
if (called->HasSameDexCacheResolvedMethods(caller)) {
caller->SetDexCacheResolvedMethod(called->GetDexMethodIndex(), called);
} else {
被调用方法和调用方法在不在同一个DexFile的情况
// Calling from one dex file to another, need to compute the method index appropriate to
// the caller's dex file. Since we get here only if the original called was a runtime
// method, we've got the correct dex_file and a dex_method_idx from above.
DCHECK_EQ(caller->GetDexFile(), dex_file);
StackHandleScope<1> hs(self);
MethodHelper mh(hs.NewHandle(called));
uint32_t method_index = mh.FindDexMethodIndexInOtherDexFile(*dex_file, dex_method_idx);
if (method_index != DexFile::kDexNoIndex) {
caller->SetDexCacheResolvedMethod(method_index, called);
}
}
} else if (invoke_type == kStatic) {
const auto called_dex_method_idx = called->GetDexMethodIndex();
// For static invokes, we may dispatch to the static method in the superclass but resolve
// using the subclass. To prevent getting slow paths on each invoke, we force set the
// resolved method for the super class dex method index if we are in the same dex file.
// b/19175856
if (called->GetDexFile() == dex_file && dex_method_idx != called_dex_method_idx) {
called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called);
}
}
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(soa.Self());
Handle called_class(hs.NewHandle(called->GetDeclaringClass()));
linker->EnsureInitialized(called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
code = called->GetEntryPointFromQuickCompiledCode();
} else if (called_class->IsInitializing()) {
if (invoke_type == kStatic) {
// Class is still initializing, go to oat and grab code (trampoline must be left in place
// until class is initialized to stop races between threads).
code = linker->GetQuickOatCodeFor(called);
} else {
// No trampoline for non-static methods.
code = called->GetEntryPointFromQuickCompiledCode();
}
} else {
DCHECK(called_class->IsErroneous());
}
}
CHECK_EQ(code == NULL, self->IsExceptionPending());
// Fixup any locally saved objects may have moved during a GC.
visitor.FixupReferences();
// Place called method in callee-save frame to be placed as first argument to quick method.
//将真正的实现ArtMethod保存到sp寄存器中
sp->Assign(called);
return code;
}
在前面可以看到,从Java代码调用到一个Java方法时,ART或为我们提供了桥接函数以进行跳转,实现从解释器模式(本地机器指令)到本地机器指令(解释器模式)的转换,或通过DexCache进行间接解析调用。从Java代码调用一个Native方法时,ART为我们提供了art_jni_dlsym_lookup_stub方法供我们找到正确的jni方法执行。还用一种情况是Native层调用Java代码(见于jni编程或反射机制)。这是怎么实现的呢?下面选取个最简单的函数CallVoidMethod进行说明。
我们了解到,CallVoidMethod的参数env对应当前的JNI上下文即JNIEnv,一个线程关联一个JNIEnv;参数obj对应Java被调用方法所属的对象;mid对应Java被调用方法的id;之后时这个被调用方法的参数。实际上这个jmethodID即是Art内部表示方法ArtMethod的地址,同时jobject时Art内部的Object地址经过转换过后得到的一个地址,稍后可以看到,使用ScopedObjectAccessAlreadyRunnable::Decode即可拿回这个Object的地址值。
/art/runtime/jni_internal.cc
static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccess soa(env);
InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap);
va_end(ap);
}
/art/runtime/reflection.cc
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj, jmethodID mid, va_list args) {
// We want to make sure that the stack is not within a small distance from the
// protected region in case we are calling into a leaf function whose stack
// check has been elided.
if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
ThrowStackOverflowError(soa.Self());
return JValue();
}
mirror::Object* receiver = soa.Decode(obj);
mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);//拿到ArtMethod的描述字符串
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);//构造一个用于标识的数组
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
return result;
}
/art/runtime/reflection.cc
static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
mirror::ArtMethod* method, ArgArray* arg_array, JValue* result,
const char* shorty)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
if (UNLIKELY(soa.Env()->check_jni)) {
CheckMethodArguments(method, args);
}
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
}
/art/runtime/mirror/art_method.cc
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
ThrowStackOverflowError(self);
return;
}
if (kIsDebugBuild) {
self->AssertThreadSuspensionIsAllowable();
CHECK_EQ(kRunnable, self->GetState());
CHECK_STREQ(GetShorty(), shorty);
}
// Push a transition back into managed code onto the linked list in thread.
ManagedStack fragment;
self->PushManagedStackFragment(&fragment);
Runtime* runtime = Runtime::Current();
// Call the invoke stub, passing everything as arguments.
if (UNLIKELY(!runtime->IsStarted())) {
//虚拟机尚未启动,使用解释器模式运行
if (IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result);
} else {
Object* receiver = reinterpret_cast*>(&args[0])->AsMirrorPtr();
art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args + 1, result);
}
} else {
const bool kLogInvocationStartAndReturn = false;
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
#if defined(ART_USE_PORTABLE_COMPILER)
bool have_portable_code = GetEntryPointFromPortableCompiledCode() != nullptr;
#else
bool have_portable_code = false;
#endif
//如前面所述,have_quick_code为true,这些quickCode或者为真正的本地机器指令,或者为延迟解析的蹦床函数artQuickResolutionTrampoline
if (LIKELY(have_quick_code || have_portable_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Invoking '%s' %s code=%p", PrettyMethod(this).c_str(),
have_quick_code ? "quick" : "portable",
have_quick_code ? GetEntryPointFromQuickCompiledCode()
#if defined(ART_USE_PORTABLE_COMPILER)
: GetEntryPointFromPortableCompiledCode());
#else
: nullptr);
#endif
}
if (!IsPortableCompiled()) {
#ifdef __LP64__
if (!IsStatic()) {
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
} else {
(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
}
#else
//以下内容考虑32位的情况
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
#endif
} else {
(*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
}
if (UNLIKELY(self->GetException(nullptr) == Thread::GetDeoptimizationException())) {
// Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
self->ClearException();
ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);
self->SetTopOfStack(nullptr, 0);
self->SetTopOfShadowStack(shadow_frame);
interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
}
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Returned '%s' %s code=%p", PrettyMethod(this).c_str(),
have_quick_code ? "quick" : "portable",
have_quick_code ? GetEntryPointFromQuickCompiledCode()
#if defined(ART_USE_PORTABLE_COMPILER)
: GetEntryPointFromPortableCompiledCode());
#else
: nullptr);
#endif
}
} else {
LOG(INFO) << "Not invoking '" << PrettyMethod(this) << "' code=null";
if (result != NULL) {
result->SetJ(0);
}
}
}
// Pop transition.
self->PopManagedStackFragment(fragment);
}
art_quick_invoke_stub是一个汇编函数。忽略以’.’为开头的指令,这些指令不影响函数的执行结果。根据AAPCS规则,调用汇编函数时,r0-r3寄存器用来传递前4个参数,多出的参数将保存在堆栈指针sp中。此外,arm的堆栈是向下生长的,每次入栈sp都要减4。
之前提到过,一个DexCache里面所有ArtMethod在初始化时都会将本地机器代码入口设置为artQuickResolutionTrampoline。在那里,我们才真正解析类,并且用真正的本地机器代码替换掉替身方法artQuickResolutionTrampoline。这样,下次在使用这个方法的时候,就可以直接获得其本地机器指令了。
/art/runtime/arch/arm/quick_entrypoints_arm.S
ENTRY art_quick_invoke_stub
//将r0, r4, r5, r9, r11, lr压栈,这里入栈的顺序时1r,r11,r9,r5,r4,r0.入完栈后sp指向r0,借传入的第一个参数ArtMethod*
push {r0, r4, r5, r9, r11, lr} @ spill regs
.save {r0, r4, r5, r9, r11, lr}
.pad #24
.cfi_adjust_cfa_offset 24
.cfi_rel_offset r0, 0
.cfi_rel_offset r4, 4
.cfi_rel_offset r5, 8
.cfi_rel_offset r9, 12
.cfi_rel_offset r11, 16
.cfi_rel_offset lr, 20
//保存sp到r11中,即ArtMethod*指针到r11
mov r11, sp @ save the stack pointer
.cfi_def_cfa_register r11
//保存r3到r9中,r3保存了传递过来的线程指针
mov r9, r3 @ move managed thread pointer into r9
#ifdef ARM_R4_SUSPEND_FLAG
mov r4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check interval
#endif
//r2为数组参数的大小,加4为ArtMethod指针预留空间,结果保存到r5
add r5, r2, #4 @ create space for method pointer in frame
//sp减去r5的结果16B对齐后再写回到sp
sub r5, sp, r5 @ reserve & align *stack* to 16 bytes: native calling
and r5, #0xFFFFFFF0 @ convention only aligns to 8B, so we have to ensure ART
mov sp, r5 @ 16B alignment ourselves.
//sp加4写到r0,r0的值将作为memcpy的dest参数,此时r1为传入的数组指针参数args,r2为数组参数的大小
add r0, sp, #4 @ pass stack pointer + method ptr as dest for memcpy
//bl跳转到memcpy
bl memcpy @ memcpy (dest, src, bytes)
//恢复之前保存的ArtMethod*指针到r0
ldr r0, [r11] @ restore method*
//分别加载数组指针参数的3个到r1,r2,r3中
ldr r1, [sp, #4] @ copy arg value for r1
ldr r2, [sp, #8] @ copy arg value for r2
ldr r3, [sp, #12] @ copy arg value for r3
//将sp所在的预留的ArtMethod*指针置null
mov ip, #0 @ set ip to 0
str ip, [sp] @ store NULL for method* at bottom of frame
//加载ArtMethod里面本地代码的偏移到ip
ldr ip, [r0, #METHOD_QUICK_CODE_OFFSET_32] @ get pointer to the code
//跳转到ArtMethod里面本地代码
blx ip @ call the method
//r11恢复到sp中
mov sp, r11 @ restore the stack pointer
//将入参result指针加载到ip中
ldr ip, [sp, #24] @ load the result pointer
//r0和r1的值写入到result指针
strd r0, [ip] @ store r0/r1 into result pointer
//将r0, r4, r5, r9, r11, lr出栈
pop {r0, r4, r5, r9, r11, lr} @ restore spill regs
.cfi_restore r0
.cfi_restore r4
.cfi_restore r5
.cfi_restore r9
.cfi_restore lr
.cfi_adjust_cfa_offset -24
//回到调用函数处继续运行
bx lr
END art_quick_invoke_stub
METHOD_QUICK_CODE_OFFSET_32是一个宏定义,值为44。这个函数究竟跳转到什么地方去了?有一处地方可以帮我们确定入口位置:
/art/runtime/mirror/object_test.cc
//METHOD_QUICK_CODE_OFFSET_32理应和ntryPointFromQuickCompiledCodeOffset返回的本地机器指令偏移地址相同
EXPECT_EQ(METHOD_QUICK_CODE_OFFSET_32,
ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value());
EntryPointFromQuickCompiledCodeOffset 就是返回了entry_point_from_quick_compiled_code_ 成员在ArtMethod的偏移值。这个偏移值对应的绝对地址就是真正的函数入口,是由linkCode设置的。
/art/runtime/mirror/art_method.h
static MemberOffset EntryPointFromQuickCompiledCodeOffset(size_t pointer_size) {
return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
}