Android类加载流程

背景

由于前前前阵子写了个壳,得去了解类的加载流程,当时记了一些潦草的笔记。这几天把这些东西简单梳理了一下,本文分析的代码基于Android8.1.0源码。

流程分析

从loadClass开始,我们来看下Android中类加载的流程

/libcore/ojluni/src/main/java/java/lang/ClassLoader.java::loadClass

loadClass流程如下:

Android类加载流程

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这个函数做了许多工作,相当于底层类加载逻辑的分发器,整体逻辑如下图:

Android类加载流程

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));     }   } } 

发表评论

相关文章