<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
由於前前前陣子寫了個殼,得去了解類的載入流程,當時記了一些潦草的筆記。這幾天把這些東西簡單梳理了一下,本文分析的程式碼基於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)); } } }
到此這篇關於Android類載入流程分析的文章就介紹到這了,更多相關Android類載入內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45