背景
由于前前前阵子写了个壳,得去了解类的加载流程,当时记了一些潦草的笔记。这几天把这些东西简单梳理了一下,本文分析的代码基于Android8.1.0源码。
流程分析
从loadClass开始,我们来看下Android中类加载的流程
/libcore/ojluni/src/main/java/java/lang/ClassLoader.java::loadClass
loadClass流程如下:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } return c; }
/libcore/ojluni/src/main/java/java/lang/ClassLoader.java::findClass
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
ClassLoader类的findClass是没有实际查找代码的,所以调用findClass其实是调用其实现类的findClass函数,例如:BaseDexClassLoader
/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java::findClass
每个BaseDexClassLoader都持有一个DexPathList,BaseDexClassLoader的findClass类调用了DexPathList的findClass。
@Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ClassNotFoundException cnfe = new ClassNotFoundException( "Didn't find class "" + name + "" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } return c; }
/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java::findClass
遍历所有dexElements,并调用Element类的findClass。
public Class<?> findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { Class<?> clazz = element.findClass(name, definingContext, suppressed); if (clazz != null) { return clazz; } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; }
题外话,dexElements对象其实是DexPathList$Element类的数组,用于存储已加载的dex或者jar的信息。
/libcore/dalvik/src/main/java/dalvik/system/DexPathList$Element::findClass
Element的findClass,又去调用DexFile类的loadClassBinaryName,可以理解为在单独的dex或者jar对象中加载类
public Class<?> findClass(String name, ClassLoader definingContext, List<Throwable> suppressed) { return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null; }
libcoredalviksrcmainjavadalviksystemDexFile.java::loadClassBinaryName
去调用defineClass函数
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, this, suppressed); }
libcoredalviksrcmainjavadalviksystemDexFile.java::defineClass
调用defineClassNative,准备进入Native层
private static Class defineClass(String name, ClassLoader loader, Object cookie, DexFile dexFile, List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie, dexFile); } catch (NoClassDefFoundError e) { if (suppressed != null) { suppressed.add(e); } } catch (ClassNotFoundException e) { if (suppressed != null) { suppressed.add(e); } } return result; }
artruntimenativedalvik_system_DexFile.cc::DexFile_defineClassNative
检查dex是否加载,类名是否合理,并遍历DexFile对象,查找Dex文件中的类的定义,找到就去调用ClassLinker::DefineClass函数。
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jobject cookie, jobject dexFile) { std::vector<const DexFile*> dex_files; const OatFile* oat_file; if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) { VLOG(class_linker) << "Failed to find dex_file"; DCHECK(env->ExceptionCheck()); return nullptr; } ScopedUtfChars class_name(env, javaName); if (class_name.c_str() == nullptr) { VLOG(class_linker) << "Failed to find class_name"; return nullptr; } const std::string descriptor(DotToDescriptor(class_name.c_str())); const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str())); for (auto& dex_file : dex_files) { const DexFile::ClassDef* dex_class_def = OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash); if (dex_class_def != nullptr) { ScopedObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader))); ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(*dex_file, class_loader.Get()); if (dex_cache == nullptr) { // OOME or InternalError (dexFile already registered with a different class loader). soa.Self()->AssertPendingException(); return nullptr; } ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash, class_loader, *dex_file, *dex_class_def); // Add the used dex file. This only required for the DexFile.loadClass API since normal // class loaders already keep their dex files live. class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile), class_loader.Get()); if (result != nullptr) { VLOG(class_linker) << "DexFile_defineClassNative returning " << result << " for " << class_name.c_str(); return soa.AddLocalReference<jclass>(result); } } } VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str(); return nullptr; }
artruntimeclass_linker.cc::DefineClass
DefineClass这个函数做了许多工作,相当于底层类加载逻辑的分发器,整体逻辑如下图:
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); ...... // Get the real dex file. This will return the input if there aren't any callbacks or they do // nothing. DexFile const* new_dex_file = nullptr; DexFile::ClassDef const* new_class_def = nullptr; // TODO We should ideally figure out some way to move this after we get a lock on the klass so it // will only be called once. Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor, klass, class_loader, dex_file, dex_class_def, &new_dex_file, &new_class_def); // Check to see if an exception happened during runtime callbacks. Return if so. if (self->IsExceptionPending()) { return nullptr; } ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get()); if (dex_cache == nullptr) { self->AssertPendingException(); return nullptr; } klass->SetDexCache(dex_cache); SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get()); // Mark the string class by setting its access flag. if (UNLIKELY(!init_done_)) { if (strcmp(descriptor, "Ljava/lang/String;") == 0) { klass->SetStringClass(); } } ObjectLock<mirror::Class> lock(self, klass); klass->SetClinitThreadId(self->GetTid()); // Make sure we have a valid empty iftable even if there are errors. klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); // Add the newly loaded class to the loaded classes table. ObjPtr<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); } // Load the fields and other things after we are inserted in the table. This is so that we don't // end up allocating unfree-able linear alloc resources and then lose the race condition. The // other reason is that the field roots are only visited from the class table. So we need to be // inserted before we allocate / fill in these fields. LoadClass(self, *new_dex_file, *new_class_def, klass); if (self->IsExceptionPending()) { VLOG(class_linker) << self->GetException()->Dump(); // An exception occured during load, set status to erroneous while holding klass' lock in case // notification is necessary. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); if (!LoadSuperAndInterfaces(klass, *new_dex_file)) { // Loading failed. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } CHECK(klass->IsLoaded()); // At this point the class is loaded. Publish a ClassLoad event. // Note: this may be a temporary class. It is a listener's responsibility to handle this. Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass); // Link the class (if necessary) CHECK(!klass->IsResolved()); // TODO: Use fast jobjects? auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr); if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { // Linking failed. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } self->AssertNoPendingException(); CHECK(h_new_class != nullptr) << descriptor; CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor; // Instrumentation may have updated entrypoints for all methods of all // classes. However it could not update methods of this class while we // were loading it. Now the class is resolved, we can update entrypoints // as required by instrumentation. if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { // We must be in the kRunnable state to prevent instrumentation from // suspending all threads to update entrypoints while we are doing it // for this class. DCHECK_EQ(self->GetState(), kRunnable); Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get()); } /* * 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. */ Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class); // Notify native debugger of the new class and its layout. jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get()); return h_new_class.Get(); }
artruntimeclass_linker.cc::SetupClass
SetupClass设置类的一些基本字段信息。
void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle<mirror::Class> klass, ObjPtr<mirror::ClassLoader> class_loader) { CHECK(klass != nullptr); CHECK(klass->GetDexCache() != nullptr); CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus()); const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); CHECK(descriptor != nullptr); klass->SetClass(GetClassRoot(kJavaLangClass)); uint32_t access_flags = dex_class_def.GetJavaAccessFlags(); CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U); klass->SetAccessFlags(access_flags); klass->SetClassLoader(class_loader); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, nullptr); klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); klass->SetDexTypeIndex(dex_class_def.class_idx_); }
延申:mirror命名空间下的类是底层对Java层类的映射,比如:mirror::Class类就是对java.lang.Class类的映射,SetAccessFlags就是对Class类的accessFlags字段赋值。
artruntimeclass_linker.cc::InsertClass
InsertClass函数判断类是否在列表中:
- 如果在列表中,则直接返回;
- 如果没有,则添加到列表。
mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr<mirror::Class> klass, size_t hash) { if (VLOG_IS_ON(class_linker)) { ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); std::string source; if (dex_cache != nullptr) { source += " from "; source += dex_cache->GetLocation()->ToModifiedUtf8(); } LOG(INFO) << "Loaded class " << descriptor << source; } { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); ObjPtr<mirror::ClassLoader> const class_loader = klass->GetClassLoader(); ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); ObjPtr<mirror::Class> existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { return existing.Ptr(); } VerifyObject(klass); class_table->InsertWithHash(klass, hash); if (class_loader != nullptr) { // This is necessary because we need to have the card dirtied for remembered sets. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); } if (log_new_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } } if (kIsDebugBuild) { // Test that copied methods correctly can find their holder. for (ArtMethod& method : klass->GetCopiedMethods(image_pointer_size_)) { CHECK_EQ(GetHoldingClassOfCopiedMethod(&method), klass); } } return nullptr; }
artruntimeclass_linker.cc::LoadClass
LoadClass函数获取了dex文件中的classData部分,然后去调用LoadClassMembers
void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle<mirror::Class> klass) { const uint8_t* class_data = dex_file.GetClassData(dex_class_def); if (class_data == nullptr) { return; // no fields or methods - for example a marker interface } LoadClassMembers(self, dex_file, class_data, klass); }
artruntimeclass_linker.cc::LoadClassMembers
LoadClassMembers函数主要逻辑是遍历类中的所有字段和函数,然后分别调用LoadField,LoadMethod和LinkCode
void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, const uint8_t* class_data, Handle<mirror::Class> klass){ ...... LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, it.NumStaticFields()); size_t num_sfields = 0; uint32_t last_field_idx = 0u; for (; it.HasNextStaticField(); it.Next()) { uint32_t field_idx = it.GetMemberIndex(); DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { DCHECK_LT(num_sfields, it.NumStaticFields()); LoadField(it, klass, &sfields->At(num_sfields)); ++num_sfields; last_field_idx = field_idx; } } // Load instance fields. LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self, allocator, it.NumInstanceFields()); size_t num_ifields = 0u; last_field_idx = 0u; for (; it.HasNextInstanceField(); it.Next()) { uint32_t field_idx = it.GetMemberIndex(); DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { DCHECK_LT(num_ifields, it.NumInstanceFields()); LoadField(it, klass, &ifields->At(num_ifields)); ++num_ifields; last_field_idx = field_idx; } } ...... 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()) { ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); LoadMethod(dex_file, it, klass, method); LinkCode(this, method, oat_class_ptr, 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()) { ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); LoadMethod(dex_file, it, klass, method); DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); LinkCode(this, method, oat_class_ptr, class_def_method_index); class_def_method_index++; } ...... }
artruntimeclass_linker.cc::LoadField
LoadField设置ArtField结构中字段的一些值
void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass, ArtField* dst) { const uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); dst->SetAccessFlags(it.GetFieldAccessFlags()); }
artruntimeclass_linker.cc::LoadMethod
LoadMethod函数主要做设置ArtMethod结构的一些属性,比如函数的MethodIdx,CodeItem在dex文件中的偏移,函数的AccessFlag等。
void ClassLinker::LoadMethod(const DexFile& dex_file, const ClassDataItemIterator& it, Handle<mirror::Class> klass, ArtMethod* dst){ 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_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_); uint32_t access_flags = it.GetMethodAccessFlags(); ...... dst->SetAccessFlags(access_flags); }
延申:ArtMethod是存储Java函数在虚拟机内相关信息的结构,它不同于mirror命名空间下的Method类,ArtMethod在Java层没有类与之直接映射。
artruntimeclass_linker.cc::LinkCode
LinkCode函数主要功能是判断代码是否编译从而为函数设置入口代码。
static void LinkCode(ClassLinker* class_linker, ArtMethod* method, const OatFile::OatClass* oat_class, uint32_t class_def_method_index){ Runtime* const runtime = Runtime::Current(); if (runtime->IsAotCompiler()) { // The following code only applies to a non-compiler runtime. return; } // Method shouldn't have already been linked. DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); 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. const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index); oat_method.LinkMethod(method); } // Install entry point from interpreter. const void* quick_code = method->GetEntryPointFromQuickCompiledCode(); bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code); if (!method->IsInvokable()) { EnsureThrowsInvocationError(class_linker, method); return; } 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(GetQuickResolutionStub()); } else if (quick_code == nullptr && method->IsNative()) { method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); } else if (enter_interpreter) { // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); } if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. method->UnregisterNative(); if (enter_interpreter || quick_code == nullptr) { // We have a native method here without code. Then it should have either the generic JNI // trampoline as entrypoint (non-static), or the resolution trampoline (static). // TODO: this doesn't handle all the cases where trampolines may be installed. const void* entry_point = method->GetEntryPointFromQuickCompiledCode(); DCHECK(class_linker->IsQuickGenericJniStub(entry_point) || class_linker->IsQuickResolutionStub(entry_point)); } } }