Dalvik類的加載-源碼閱讀筆記

?

原文連接http://www.itdecent.cn/p/cb39b1797326

前言

本文主要研究Android dalvik虛擬機(jī)加載類的流程和機(jī)制。目的是了解Android中DEX文件結(jié)構(gòu),虛擬機(jī)如何從DEX文件中加載一個(gè)Java Class,以及到最終如何初始化這個(gè)類直至可被正常使用。

?

[Java]類的加載

在Java的世界里,所有類的加載,都由?java.lang.ClassLoader?來負(fù)責(zé)。ClassLoader是一個(gè)抽象類,它有多個(gè)實(shí)現(xiàn)類,例如?BootClassLoader?,?SystemClassLoader?以及虛擬機(jī)的具體實(shí)現(xiàn),例如在dalvik虛擬機(jī)里的實(shí)現(xiàn)為?DexClassLoader?。

需要注意的每個(gè)虛擬機(jī)對(duì)于類的加載的邏輯并不十分相同,例如hotspot虛擬機(jī)和dalvik虛擬機(jī)加載一個(gè)類的過程基本上完全不同,hotspot主要是從class文件從加載類,而dalvik是從dex文件里去加載一個(gè)類,所以這里只討論的是dalvik虛擬機(jī)里的實(shí)現(xiàn)機(jī)制。

?

雙親委派機(jī)制

不管虛擬機(jī)的具體實(shí)現(xiàn),但虛擬機(jī)spec定義的關(guān)于類的加載規(guī)范必須被實(shí)現(xiàn),例如最基礎(chǔ)的雙親委派機(jī)制。

它規(guī)定每個(gè)ClassLoader都得有一個(gè)父親ClassLoader,以此形成一個(gè)父子的多層級(jí)關(guān)系,利用這個(gè)層級(jí)關(guān)系實(shí)現(xiàn)了類的雙親委派機(jī)制。

在dalvik里的ClassLoader層級(jí)關(guān)系如下:

Bootstrap ClassLoader

System ClassLoader

Dex ClassLoader

?

在任何一個(gè)ClassLoader加載一個(gè)類的時(shí)候,都會(huì)先委托其父ClassLoader來負(fù)責(zé)加載這個(gè)類,一直遞歸到最頂層的ClassLoader,這個(gè)設(shè)計(jì)主要應(yīng)該是為了安全考慮。以保障上層的類被上層的ClassLoader來加載,而避免系統(tǒng)類被下層的ClassLoader給替換掉了,而引發(fā)安全問題。

?

例如?DexClassLoader?加載類的時(shí)候,會(huì)先委托?SystemClassLoader?來加載該類,而?SystemClassLoader?又會(huì)先讓它的父親?BootClassLoader?先來加載,如果其所有祖父?jìng)兌疾患虞d該類,才會(huì)由這個(gè)ClassLoader去加載。

具體的代碼實(shí)現(xiàn)為:

protectedClass loadClass(String name,booleanresolve)throwsClassNotFoundException? ? {// First, check if the class has already been loadedClass c = findLoadedClass(name);if(c ==null) {longt0 = System.nanoTime();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.longt1 = System.nanoTime();? ? ? ? ? ? ? ? ? ? c = findClass(name);// this is the defining class loader; record the stats}? ? ? ? ? ? }returnc;? ? }

源碼來至 : /java/lang/ClassLoader.java

源碼簡(jiǎn)單解讀:

先檢查這個(gè)類是否被加載過,如果已經(jīng)被加載過,則直接返回,不重新加載該類。(注意這里已經(jīng)加過的類的列表并沒有存儲(chǔ)到Java層面,而是直接去問native層這個(gè)類是否被加載過,它維護(hù)了所有加載過的類)

遞歸委派父ClassLoader去加載。

如果所有父ClassLoader都不加載,自己才有權(quán)利去加載該類。

先去找到該Class(具體的findClass邏輯由子類實(shí)現(xiàn))

?

類加載器(ClassLoader)

ClassLoader作為基類,關(guān)鍵的方法都交給子類具體去實(shí)現(xiàn)了,但它定義了類的加載過程:

load -> find -> define -> resolve

Class loadClass(String name,booleanresolve);Class findClass(String name);Class defineClass(String name,byte[] b,intoff,intlen, ProtectionDomain protectionDomain);voidresolveClass(Class c);

?

SystemClassLoader

SystemClassLoader是一個(gè)ClassLoader默認(rèn)的parent,即在創(chuàng)建一個(gè)ClassLoader時(shí),不傳入parent參數(shù),則默認(rèn)會(huì)使用這個(gè)SystemClassLoader作為其parent。

它其實(shí)是ClassLoader的一個(gè)靜態(tài)內(nèi)部類,里面包含了一個(gè)默認(rèn)的ClassLoader,也是由ClassLoader的靜態(tài)方法來創(chuàng)建的:

staticprivateclassSystemClassLoader{publicstaticClassLoader loader = ClassLoader.createSystemClassLoader();? ? }/**

? ? * Encapsulates the set of parallel capable loader types.

? ? */privatestaticClassLoadercreateSystemClassLoader(){? ? ? ? String classPath = System.getProperty("java.class.path",".");? ? ? ? String librarySearchPath = System.getProperty("java.library.path","");returnnewPathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());? ? }publicstaticClassLoadergetSystemClassLoader(){returnSystemClassLoader.loader;? ? }

由代碼可以看出 SystemClassLoader其實(shí)是一個(gè)全局靜態(tài)的單例類,并且它的parent為 BootClassLoader。

?

BootClassLoader

BootClassLoader也定義在ClassLoader類,但外部不能訪問到。也是一個(gè)單例類。它是ClassLoader的子類。它是唯一一個(gè)沒有parent的ClassLoader。

classBootClassLoaderextendsClassLoader{privatestaticBootClassLoader instance;@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")publicstaticsynchronizedBootClassLoadergetInstance(){if(instance ==null) {? ? ? ? ? ? instance =newBootClassLoader();? ? ? ? }returninstance;? ? }publicBootClassLoader(){super(null);? ? }// ...}

其中l(wèi)oadClass里,先查找已經(jīng)加載的類,由以下方法來實(shí)現(xiàn),該方法是native方法 :

VMClassLoader#findLoadedClass(ClassLoader cl, String name);

然后findClass是由?Class.classForName?來實(shí)現(xiàn)的,也是一個(gè)native方法。

?

?

[Android]類的加載

加載Dex(DexClassLoader)

在dalvik里面的ClassLoader主要是由?DexClassLoader?和?PathClassLoader?來完成,這兩個(gè)類屬于?android/platform/libcore?項(xiàng)目,源碼可查看AOSP:

https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/

?

DexClassLoader 繼承?dalvik.system.BaseDexClassLoader?,只提供一個(gè)構(gòu)造器,并沒有實(shí)現(xiàn)代碼,主要代碼還是在父類里面。

我們現(xiàn)在看?BaseDexClassLoader?,它繼承于 ClassLoader. 先來看它的構(gòu)造器:

publicBaseDexClassLoader(String dexPath, File optimizedDirectory,

? ? ? ? ? ? String librarySearchPath, ClassLoader parent){super(parent);this.pathList =newDexPathList(this, dexPath, librarySearchPath,null);? ? }

這里需要傳四個(gè)參數(shù),也基本能看出這個(gè)類的主要結(jié)構(gòu)。

dexPath: 這個(gè)參數(shù)的名字雖然是一個(gè)path結(jié)尾的,但不是傳一個(gè)目錄,而是傳?.dex?.zip,.apk,.jar的文件絕對(duì)路徑,但可以一次傳多個(gè),用?:?作為分隔即可。

optimizedDirectory: 這個(gè)目錄是dex的優(yōu)化目錄,必須是當(dāng)前用戶,并且可寫.(也可以為?null,系統(tǒng)會(huì)使用默認(rèn)目錄,一般是 /data/app/dalvik-cache)

librarySearchPath: native庫的搜索目錄,可以用?:?作為分隔符傳入多個(gè)目錄,也可以傳入?null?.

parent: parent ClassLoader

這里面關(guān)鍵的參數(shù)都傳給了?DexPathList?對(duì)象了,可見大多數(shù)邏輯都會(huì)交給它來處理。

?

findClass

@OverrideprotectedClass findClass(String name)throwsClassNotFoundException {? ? ? ? List suppressedExceptions =newArrayList();? ? ? ? Class c = pathList.findClass(name, suppressedExceptions);if(c ==null) {? ? ? ? ? ? ClassNotFoundException cnfe =newClassNotFoundException("Didn't find class \""+ name +"\" on path: "+ pathList);for(Throwable t : suppressedExceptions) {? ? ? ? ? ? ? ? cnfe.addSuppressed(t);? ? ? ? ? ? }throwcnfe;? ? ? ? }returnc;? ? }

如推測(cè)的一樣,具體的邏輯都交給 DexPathList 去實(shí)現(xiàn)了。那么接下來我們就來研究它。需要注意的是?BaseDexClassLoader?只重寫了?findClass?這個(gè)方法,而沒有重寫?loadClass?,?defineClass,resolveClass?!

?

Dex列表(DexPathList)

這個(gè)類時(shí)傳入的 dexPath 的抽象,因?yàn)閐exPath可能會(huì)傳入用?:?分隔的多個(gè)apk文件,而每個(gè)apk文件中又可能有多個(gè)dex文件,因此 DexPathList 包含了所有apk文件里面的所有dex文件的封裝。并將每個(gè)Dex文件抽象成?DexFile?對(duì)象,包裹在?DexPathList#Element?列表中。

privateElement[] dexElements;

在處理dexPath的時(shí)候,首先從?:?分隔符split成數(shù)組,然后遍歷這些文件,將其解析成?DexFile?對(duì)象,并封裝到 Element 中。

for(File file : files) {// ...if(name.endsWith(DEX_SUFFIX)) {// .dexDexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);if(dex !=null) {? ? ? ? ? ? elements[elementsPos++] =newElement(dex,null);? ? ? ? }? ? }else{// .zip .jar// ...DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);// ...}// ...}

?

在調(diào)用?findClass?的時(shí)候,會(huì)遍歷這些DexFile文件,從Dex中尋找具體的class:

for(Element element : dexElements) {? ? Class clazz = element.findClass(name, definingContext, suppressed);if(clazz !=null) {returnclazz;? ? }}

?

除了傳入一個(gè)dexPath,還可以傳入一個(gè)?ByteBuffer?數(shù)組,每個(gè)byteBuffer里面包含了一個(gè)dex文件的字節(jié)流。并且?optimizedDirectory?參數(shù)是可以為?NULL?的。

?

除了dex文件,DexPathList還負(fù)責(zé)管理所有的native庫,并將其也維護(hù)在一個(gè)列表中:

/** List of native library path elements. */privatefinalNativeLibraryElement[] nativeLibraryPathElements;

nativeLibrary的searchPath可以是一個(gè)普通路徑,也可以是一個(gè)zip的路徑。

當(dāng)查找一個(gè)library的時(shí)候,會(huì)去從這些目錄下尋找文件,例如:findLibrary(String filename) ,那么會(huì)去遍歷這些目錄,直到找到 path/filename 存在(并可讀)的時(shí)候,則返回該library文件(如so文件)

?

尋找Class文件(DexFile)

dalvik.system.DexFile?這個(gè)類是對(duì) Dex文件的抽象,具體在Dex文件中尋找Class定義的工作,則是由這個(gè)類來處理。

首先它會(huì)將dex文件打開,并讀取成VM cookie object對(duì)象(具體的讀取dex邏輯是由native方法實(shí)現(xiàn))。

?

publicClassloadClass(String name, ClassLoader loader){? ? String slashName = name.replace('.','/');returnloadClassBinaryName(slashName, loader,null);}privatestaticnativeClassdefineClassNative(String name, ClassLoader loader, Object cookie,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DexFile dexFile)throwsClassNotFoundException, NoClassDefFoundError;

至于真正的?loadClass()?邏輯其實(shí)還是在defineClassNative()?這個(gè)native方法里完成的。

?

到此為止,Java層的關(guān)于關(guān)于class的加載邏輯基本已經(jīng)了解,具體的很多工作都是native層是去完成,因此接下來我們來研究native層的。

?

?

[c++]類的加載

讀取DEX文件(openDexFile)

這里我們接著上面 DexFile 的 native 方法來看,c++的源碼在:

\dalvik2\vm\native\dalvik_system_DexFile.cpp

首先在native層會(huì)將dex文件打開并映射到內(nèi)存中:

staticvoidDalvik_dalvik_system_DexFile_openDexFile(constu4* args,? ? JValue* pResult){// ...DexOrJar* pDexOrJar =NULL;// ...if(hasDexExtension(sourceName)? ? ? ? ? ? && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile,false) ==0) {? ? ? ? ALOGV("Opening DEX file '%s' (DEX)", sourceName);? ? ? ? pDexOrJar = (DexOrJar*)malloc(sizeof(DexOrJar));? ? ? ? pDexOrJar->isDex =true;? ? ? ? pDexOrJar->pRawDexFile = pRawDexFile;? ? ? ? pDexOrJar->pDexMemory =NULL;? }// ...RETURN_PTR(pDexOrJar);}

讀取dex文件的邏輯在?dvmRawDexFileOpen?函數(shù)中,它會(huì)去讀取dex文件,并將其內(nèi)部數(shù)據(jù)映射到一塊只讀的共享內(nèi)存中去。具體負(fù)責(zé)內(nèi)存映射的邏輯在?dexFileParse?函數(shù)中。

?

加載類的流程

首先來看,虛擬機(jī)對(duì)于一個(gè)類的加載流程,分為如下幾個(gè)狀態(tài),從中大概能看到整個(gè)流程都經(jīng)過了什么。

類的加載狀態(tài) ClassStatus :

namevaluenote

CLASS_ERROR-1

CLASS_NOTREADY0

CLASS_IDX1loaded, DEX idx in super or interfaces

CLASS_LOADED2DEX idx values resolved

CLASS_RESOLVED3part of linking

CLASS_VERIFYING4in the process of being verified

CLASS_VERIFIED5logically part of linking, done pre-init

CLASS_INITIALIZING6class init in process

CLASS_INITIALIZED7ready to go

?

?

這個(gè)函數(shù)比較關(guān)鍵,它主要負(fù)責(zé)在dex文件去中查找和加載class,但也比較長(zhǎng),因此在此做省略操作,只保留關(guān)鍵部分,我們從中提取關(guān)鍵點(diǎn)進(jìn)行分析:

staticvoid Dalvik_dalvik_system_DexFile_defineClass(constu4* args,? ? JValue* pResult){// ...if(pDexOrJar->isDex)? ? ? ? pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);elsepDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);// ...clazz = dvmDefineClass(pDvmDex, descriptor, loader);// ...}

這個(gè)函數(shù)前面做了一些轉(zhuǎn)換工作就不分析了,首先它會(huì)去拿到可以映射到內(nèi)存的dex文件,關(guān)鍵的一句在于調(diào)用?dvmDefineClass?函數(shù),該函數(shù)會(huì)從dex里去查找和加載class。

?

該函數(shù)定義在?dalvik2/vm/oo/Class.cpp?文件中,具體的函數(shù)為:

staticClassObject*findClassNoInit(constchar* descriptor, Object* loader,? ? DvmDex* pDvmDex){// 在已經(jīng)加載過的類中去尋找,如果已經(jīng)加載過,則不用再進(jìn)行加載了clazz = dvmLookupClass(descriptor, loader,true);if(clazz == null) {// 在Dex文件中尋找Class定義pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);// 找到了Class的定義后,將其加載成ClassObject對(duì)象ClassObject* clazz = loadClassFromDex(pDvmDex, pClassDef, loader);// ...// 記錄該類到已加載過類的hash table中,便于下次檢查dvmAddClassToHash(clazz);// ...// 鏈接這個(gè)ClassdvmLinkClass(clazz);? ? }}

這個(gè)函數(shù)則是最為核心的邏輯,我們?cè)敿?xì)來分析:

首先,查找已經(jīng)加載過的類,所有加載過的類都會(huì)將其classDescriptor(類的描述,類似于?Ljava.lang.Object;?這種字符串),將這個(gè)類的描述符進(jìn)行hash,并作為加載的ClassObject的鍵丟到一個(gè)hash table里面去,每次加載一個(gè)類之前,都會(huì)先去這個(gè)hash table里面去找一下,看之前有沒有加載過該類,如果加載過則不重復(fù)加載,直接從這個(gè)hash table里面返回ClassObject對(duì)象。否則才會(huì)去加載該類。

?

第一步:尋找

如果這個(gè)類從來沒有加載過,則從Dex文件中去尋找Class的定義,這個(gè)過程是交給?DexFile.cpp?源碼里的?dexFindClass?函數(shù)去完成的。

這個(gè)類也比較關(guān)鍵,也不太好理解,因?yàn)橘N出完整的代碼,我們仔細(xì)分析:

/*

* 通過類描述符查找一個(gè)類的定義

*

* 類描述符"descriptor"應(yīng)該例如:"Landroid/debug/Stuff;".

*/constDexClassDef*dexFindClass(constDexFile* pDexFile,constchar* descriptor){constDexClassLookup* pLookup = pDexFile->pClassLookup;? ? u4 hash;intidx, mask;? ? hash = classDescriptorHash(descriptor);? ? mask = pLookup->numEntries -1;? ? idx = hash & mask;/*

? ? * 遍歷DexClassLookup,直到找到Class的定義

? ? */while(true) {intoffset;? ? ? ? offset = pLookup->table[idx].classDescriptorOffset;if(offset ==0)returnNULL;// 先比對(duì)類描述符的hash值if(pLookup->table[idx].classDescriptorHash == hash) {constchar* str;? ? ? ? ? ? str = (constchar*) (pDexFile->baseAddr + offset);// hash值匹配后,再比對(duì)類描述符的字符串if(strcmp(str, descriptor) ==0) {// 找到匹配指定描述符的類return(constDexClassDef*)? ? ? ? ? ? ? ? ? ? (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);? ? ? ? ? ? }? ? ? ? }? ? ? ? idx = (idx +1) & mask;? ? }}

該函數(shù)主要描述了如何從一個(gè)dex文件中去尋找一個(gè)類的定義。

?

第二步:裝載

根據(jù)找到的類的定義ClassDef,加載這個(gè)類的信息,去dex文件中去創(chuàng)建一個(gè) ClassObject 對(duì)象,并將其存在已經(jīng)加載過的類的hash table里面,下次find這個(gè)class的時(shí)候,就不會(huì)重復(fù)去加載這個(gè)class了,直接從hash table里面拿。

這個(gè)過程由?loadClassFromDex0?函數(shù)負(fù)責(zé):

/*

* Helper for loadClassFromDex, which takes a DexClassDataHeader and

* encoded data pointer in addition to the other arguments.

*/staticClassObject* loadClassFromDex0(DvmDex* pDvmDex,constDexClassDef* pClassDef,constDexClassDataHeader* pHeader,constu1* pEncodedData, Object* classLoader){? ? newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);? ? dvmSetClassSerialNumber(newClass);// 類的簽名newClass->descriptor = descriptor;// 類的狀態(tài) -> LoadednewClass->status = CLASS_IDX;// 父類newClass->super = (ClassObject*) pClassDef->superclassIdx;// 接口列表pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);// 靜態(tài)變量(并設(shè)置為默認(rèn)值0或null)newClass->sfieldCount = count;for(i =0; i < count; i++) {? ? ? ? dexReadClassDataField(&pEncodedData, &field, &lastIndex);? ? ? ? loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);? ? }// 成員變量for(i =0; i < count; i++) {? ? ? ? dexReadClassDataField(&pEncodedData, &field, &lastIndex);? ? ? ? loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);? ? }? ? dvmLinearReadOnly(classLoader, newClass->ifields);// 成員方法newClass->directMethodCount = count;? ? newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,? ? ? ? ? ? count * sizeof(Method));for(i =0; i < count; i++) {? ? ? ? dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);? ? ? ? loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);? ? }? ? dvmLinearReadOnly(classLoader, newClass->directMethods);// 虛方法(父類方法)newClass->virtualMethodCount = count;? ? newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,? ? ? ? ? ? count * sizeof(Method));for(i =0; i < count; i++) {? ? ? ? dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);? ? ? ? loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);? ? }? ? dvmLinearReadOnly(classLoader, newClass->virtualMethods);// 字節(jié)碼newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);}

注意這里的classLoader就是用來加載類的,一般都是java層傳過來的。如果loader是null的話,則會(huì)去加載系統(tǒng)class,例如?java.lang.Class?類。

這里會(huì)在 heap 里分配一塊內(nèi)存(使用 dvmMalloc 函數(shù)),來創(chuàng)建一個(gè) ClassObject 對(duì)象,用于放置類的信息。包括:

descriptor 類的簽名

類的字段對(duì)象 fieldObject

類的狀態(tài)為:CLASS_IDX

類的父類

類的接口列表

類的靜態(tài)成員變量信息(設(shè)置標(biāo)準(zhǔn)默認(rèn)值:null和0)

類的成員變量信息

類的方法

類的虛方法(父類方法)

類的字節(jié)碼

?

第三步:鏈接

booldvmLinkClass(ClassObject* clazz){ }

link的過程又分為:

加載 LOADED,父類和接口都為NULL

鏈接 RESOLVED,將父類和接口的dex idx鏈接替換成真正的引用

驗(yàn)證 VERIFIED,檢查一個(gè)類的,例如:

如果該類是 java.lang.Object,則不能再有父類

如果不是java.lang.Object, 則必須有父類

不能繼承final類

不能繼承interface

只能繼承public的類或同一個(gè)包下的類

等等

所有這些邏輯都在 java_lang_Class.cpp里的:

?

第四步:初始化

booldvmInitClass(CLassObject* clazz){ }

初始化之前,必須確保該類已經(jīng)被驗(yàn)證過(VERIFIED)如果沒有,則立即先驗(yàn)證它。

如果沒有優(yōu)化過類,則先優(yōu)化類(optimize),

然后做一些驗(yàn)證工作,和線程安全的操作(因?yàn)榭赡芏鄠€(gè)線程同時(shí)引發(fā)初始化某個(gè)類,所以會(huì)使用當(dāng)前類鎖對(duì)初始化過程加鎖)。

接下來就開始初始化過程:

先遞歸的初始化父類?super class,它的父類的父類,直到它們都先被初始化了。****

初始化靜態(tài)常量?static final,它會(huì)從dex里面將靜態(tài)變量的值拿出去(根據(jù)偏移量),賦值到類的靜態(tài)變量上。

執(zhí)行靜態(tài)區(qū)的代碼?static {},所有寫在靜態(tài)區(qū)的代碼都會(huì)合并到靜態(tài)方法里面,該方法的名字為?,簽名為?()V。它被當(dāng)做一個(gè)正常的靜態(tài)方法被調(diào)用。dvmCallMethod()

到此,如果沒有遇到異常,則該類被視為初始化完畢,可以正常使用,狀態(tài)也變?yōu)?CLASS_INITIALIZED.

?

DexFile文件結(jié)構(gòu)

我們對(duì)虛擬機(jī)加載一個(gè)類的整個(gè)過程基本有了一定了解,因?yàn)樵诩虞d的過程中,基本都是在和 DexFile 文件在打交道,因此作為擴(kuò)展知識(shí),我們也順便了解 DexFile 的文件結(jié)構(gòu)。

?

首先要明確 Android 之所以使用 Dex 文件來代替Java里的Jar包文件,主要是為了解決在手機(jī)這種存儲(chǔ)空間有限的設(shè)備上能更進(jìn)一步的壓縮空間的考慮。

傳統(tǒng)的jar包文件,里面存儲(chǔ)了一個(gè)個(gè)分散的class文件,而dex文件其實(shí)是將一個(gè)個(gè)分散的class文件合并成一個(gè)文件。

?

因此dex的文件結(jié)構(gòu)基本上和class的文件結(jié)構(gòu)非常相關(guān)或相似,所有先來看見看class文件的結(jié)構(gòu):

一個(gè)class文件基本上可以分為幾塊內(nèi)容:

基本信息(如magic,minor version, major version, access flags, this class, super class等信息)

常量池 Constant Pool,它包含了所有基本類型,string,類名,字段名,方法名,類型等常量

接口列表 Interfaces ,它是該類的所有實(shí)現(xiàn)接口的列表,包含了所有接口的class,其中className也是指向常量池里的一個(gè)classInfo

字段列表 fields, 它包括該類所有字段的列表,每個(gè)字段包括三個(gè)信息:Name字段名,Descriptor字段類型簽名,access flags訪問權(quán)限(public/private/protected/static/final等)

方法列表 methods, 它包括該類的所有定義的方法的列表,每個(gè)方法包括:Name方法名,Descriptor方法簽名,access flags訪問權(quán)限,以及具體的字節(jié)碼code.

?

接下來我們先來看 DexFile 的定義:

structDexFile{/* directly-mapped "opt" header */constDexOptHeader* pOptHeader;// DEX文件頭指針,包含所有指針的偏移量和長(zhǎng)度constDexHeader*? ? pHeader;// 字符串列表指針,UTF-16編碼constDexStringId*? pStringIds;// 類型列表指針constDexTypeId*? ? pTypeIds;// 字段列表指針constDexFieldId*? pFieldIds;// 方法列表指針constDexMethodId*? pMethodIds;// 函數(shù)原型數(shù)據(jù)指針,方法聲明的字符串,返回類型和參數(shù)列表constDexProtoId*? pProtoIds;// 類的定義列表指針,類的信息,包括接口,超類,類信息,靜態(tài)變量偏移量等constDexClassDef*? pClassDefs;// 靜態(tài)連接數(shù)據(jù)constDexLink*? ? ? pLinkData;/*

? ? * These are mapped out of the "auxillary" section, and may not be

? ? * included in the file.

? ? */constDexClassLookup* pClassLookup;constvoid*? ? ? ? pRegisterMapPool;// RegisterMapClassPool/* points to start of DEX file data */constu1*? ? ? ? ? baseAddr;/* track memory overhead for auxillary structures */intoverhead;/* additional app-specific data structures associated with the DEX *///void*? ? ? ? ? ? ? auxData;};

可以看出來基本一個(gè)Dex就是將多個(gè)Class的信息整合到一起,這樣的好處是,例如常量池這些都可以共享,從而減少了整體的存儲(chǔ)空間。

?

?

結(jié)語

看完dalvik虛擬機(jī)對(duì)于類的加載流程的相關(guān)源碼以后,對(duì)一個(gè)類是如何被加載到虛擬機(jī)有了一個(gè)新的認(rèn)知,并從中看到Android dalvik是如何實(shí)現(xiàn)Java虛擬機(jī)spec的等一些細(xì)節(jié),和hotspot的實(shí)現(xiàn)還是有比較大的區(qū)別的,對(duì)Dex文件,Class文件的格式也有了一個(gè)更直觀的了解。下一步需要了解的應(yīng)該是虛擬機(jī)內(nèi)存相關(guān)的部分。

?

源碼索引:

java/lang/ClassLoader.java

? ? loadClass(findClass)

? ? defineClass

? ? resolveClass

dalvik.system.BaseDexClassLoader.javafindClass

dalvik2/vm/oo/class.cpploadMethodFromDex

dalvik2/vm/native.cpp? ? dvmResolveNativeMethod? ? unregisterJNINativeMethods

dalvik2/vm/jni.cpp

? ? dvmRegisterJNIMethod

?

參考資料:

Dalvik2源碼

Dalvik libcore源碼

Dalvik類加載機(jī)制分析

Android-Dex文件格式解析

Dalvik虛擬機(jī)中DexClassLookup結(jié)構(gòu)解析

Android安全–從defineClassNative看類的加載過程

Dex動(dòng)態(tài)加載的C語言部分

Android安全–Dex文件格式詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容