Android運(yùn)行時(shí)ART加載類和方法的過程分析資料_第1頁
Android運(yùn)行時(shí)ART加載類和方法的過程分析資料_第2頁
Android運(yùn)行時(shí)ART加載類和方法的過程分析資料_第3頁
Android運(yùn)行時(shí)ART加載類和方法的過程分析資料_第4頁
Android運(yùn)行時(shí)ART加載類和方法的過程分析資料_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

Android運(yùn)行時(shí)ART加載類和方法的過程分析在前一篇文章中,我們通過分析OAT文件的加載過程,認(rèn)識了OAT文件的格式,其中包含了原始的DEX文件。既然ART運(yùn)行時(shí)執(zhí)行的都是翻譯DEX字節(jié)碼后得到的本地機(jī)器指令了,為什么還需要在 OAT文件中包含 DEX文件,并且將它加載到內(nèi)存去呢?這是因?yàn)?ART運(yùn)行時(shí)提供了 Java虛擬機(jī)接口,而要實(shí)現(xiàn) Java虛擬機(jī)接口不得不依賴于 DEX文件。本文就通過分析 ART運(yùn)行時(shí)加載類及其方法的過程來理解 DEX文件的作用。在前面這篇文章的最后,我們簡單總結(jié)了 ART運(yùn)行時(shí)查找類方法的本地機(jī)器指令的過程,如圖1所示:為了方便描述,我們將 DEX文件中描述的類和方法稱為 DEX類(DexClass)和DEX方法(DexMethod),而將在OAT文件中描述的類和方法稱為 OAT類(OatClass)和OAT方法(OatMethod)。接下來我們還會看到,ART運(yùn)行時(shí)在內(nèi)部又會使用另外兩個(gè)不同的術(shù)語來描述類和方法,其中將類描述為Class,而將類方法描述為ArtMethod。在圖1中,為了找到一個(gè)類方法的本地機(jī)器指令,我們需要執(zhí)行以下的操作:1.在DEX文件中找到目標(biāo) DEX類的編號,并且以這個(gè)編號為索引,在 OAT文件中找到對應(yīng)的 OAT類。在DEX文件中找到目標(biāo)DEX方法的編號,并且以這個(gè)編號為索引,在上一步找到的OAT類中找到對應(yīng)的OAT方法。使用上一步找到的OAT方法的成員變量begin_和code_offset_,計(jì)算出該方法對應(yīng)的本地機(jī)器指令。通過前面一文的學(xué)習(xí),我們可以知道,ART運(yùn)行時(shí)的入口是類的靜態(tài)成員函數(shù)main,如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片voidAndroidRuntime::start(constchar*className,constchar*options){....../*startthevirtualmachine*/JniInvocationjni_invocation;jni_invocation.Init(NULL);JNIEnv*env;if(startVm(&mJavaVM,&env)!=0){return;}....../**StartVM. ThisthreadbecomesthemainthreadoftheVM,andwillnotreturnuntiltheVMexits.*/char*slashClassName=toSlashClassName(className);jclassstartClass=env->FindClass(slashClassName);if(startClass==NULL){ALOGE("JavaVMunabletolocateclass'%s'\n",slashClassName);/*keepgoing*/}else{jmethodIDstartMeth=env->GetStaticMethodID(startClass,"main","([Ljava/lang/String;)V");if(startMeth==NULL){ALOGE("JavaVMunabletofindmain()in'%s'\n",className);/*keepgoing*/}else{env->CallStaticVoidMethod(startClass,startMeth,strArray);......}}......}這個(gè)函數(shù)定義在文件在AndroidRuntimeJava虛擬機(jī)mJavaVM及其來的描述中,我們將不區(qū)分獲得了 ART 虛擬機(jī)的

frameworks/base/core/jni/AndroidRuntime.cpp 中。類的成員函數(shù) start中,首先是通過調(diào)用函數(shù) startVm創(chuàng)建了一個(gè)JNI接口env。這個(gè)Java虛擬機(jī)實(shí)際上就是 ART運(yùn)行時(shí)。在接下ART虛擬機(jī)和ART運(yùn)行時(shí),并且認(rèn)為它們表達(dá)的是同一個(gè)概念。JNI 接口之后,就可以通過它提供的函數(shù) FindClass 和GetStaticMethodID來加載最后就可以再通過

JNI 接口提供的函數(shù)

類及其靜態(tài)成員函數(shù)CallStaticVoidMethod

main。于是,來調(diào)用類的靜態(tài)成員函數(shù)main,以及進(jìn)行到ART虛擬機(jī)里面去運(yùn)行。接下來,我們就通過分析 JNI接口FindClass和GetStaticMethodID的實(shí)現(xiàn),以便理解ART運(yùn)行時(shí)是如何查找到指定的類和方法的。在接下來的一篇文章中,我們再分析運(yùn)行時(shí)是如何通過 JNI接口CallStaticVoidMethod來執(zhí)行指定類方法的本地機(jī)器指令的。

ART在分析JNI接口FindClass和GetStaticMethodID的實(shí)現(xiàn)之前,我們先要講清楚 JNI接口是如何創(chuàng)建的。從前面一文可以知道,與 ART虛擬機(jī)主線程關(guān)聯(lián)的 JNI接口是在函數(shù)JNI_CreateJavaVM中創(chuàng)建的,如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片extern"C"jintJNI_CreateJavaVM(JavaVM**p_vm,JNIEnv**p_env,void*vm_args){......*p_env=Thread::Current()->GetJniEnv();......returnJNI_OK;}這個(gè)函數(shù)定義在文件 art/runtime/jni_internal.cc 中。調(diào)用Thread類的靜態(tài)成員函數(shù) Current獲得的是用來描述當(dāng)前線程的主線程)的一個(gè) Thread對象,再通過調(diào)用這個(gè) Thread對象的成員函數(shù)一個(gè)JNI接口,并且保存在輸出參數(shù) p_env中。

(即ARTGetJniEnv

虛擬機(jī)就獲得Thread

類的成員函數(shù)

GetJniEnv

的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODEclassPACKED(4)Thread{

上查看代碼片派生到我的代碼片public:......//JNImethodsJNIEnvExt*GetJniEnv()const{returnjni_env_;}......private:......EverythreadmayhaveanassociatedJNIenvironmentJNIEnvExt*jni_env_;......};這個(gè)函數(shù)定義在文件

art/runtime/thread.h

中。Thread類的成員函數(shù)

GetJniEnv

返回的是成員變量

jni_env_指向的一個(gè)

JNIEnvExt

對象。JNIEnvExt類是從JNIEnv類繼承下來的,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片structJNIEnvExt:publicJNIEnv{......};這個(gè)類定義在文件 art/runtime/jni_internal.h。JNIEnv類定義了JNI接口,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片typedef_JNIEnvJNIEnv;......struct_JNIEnv{/*donotrenamethis;itdoesnotseemtobeentirelyopaque*/conststructJNINativeInterface*functions;......jintGetVersion(){returnfunctions->GetVersion(this);}......};這個(gè)類定義在文件 libnativehelper/include/nativehelper/jni.h 中。在JNIEnv 類中,最重要的就是成員變量 functions 了,它指向的是一個(gè)類型為JNINativeInterface的JNI函數(shù)表。所有的 JNI接口調(diào)用都是通過這個(gè) JNI函數(shù)表來實(shí)現(xiàn)的。例如,用來獲得版本號的 JNI接口GetVersion就是通過調(diào)用 JNI函數(shù)表中的 GetVersion函數(shù)來實(shí)現(xiàn)的。那么,上述的

JNI

函數(shù)表是如何創(chuàng)建的呢?通過

JNIEnvExt

類的構(gòu)造函數(shù)可以知道答案,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片JNIEnvExt::JNIEnvExt(Thread*self,JavaVMExt*vm):......{functions=unchecked_functions=&gJniNativeInterface;......}這個(gè)函數(shù)定義在文件 art/runtime/jni_internal.cc 中。JNIEnvExt 類的構(gòu)造函數(shù)將父類 JNIEnv 的成員變量gJniNativeInterface。也就是說,JNI函數(shù)表實(shí)際是由全局變量

functions初始化為全局變量gJniNativeInterface來描述的。全局變量

gJniNativeInterface

的定義如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片constJNINativeInterfacegJniNativeInterface={NULL, //reserved0.NULL, //reserved1.NULL, //reserved2.NULL, //reserved3.JNI::GetVersion,......JNI::FindClass,......JNI::GetStaticMethodID,......JNI::CallStaticVoidMethod,......};這個(gè)全局變量定義在文件 art/runtime/jni_internal.cc 中。從這里可以看出, JNI函數(shù)表實(shí)際上是由 JNI類的靜態(tài)成員函數(shù)組成的。例如, JNI函數(shù)GetVersion是由JNI類的靜態(tài)成員函數(shù) GetVersion來實(shí)現(xiàn)的。理解了這一點(diǎn)之后, 我們就輕松地知道同接下來我們要分析的JNI接口FindClass和GetStaticMethodID分別是由JNI類的靜態(tài)成員函數(shù)FindClass和GetStaticMethodID來實(shí)現(xiàn)的。事實(shí)上,如果讀者看過這篇文章,那么對上述的JNI接口定義是一目了然的。JNI類的靜態(tài)成員函數(shù) FindClass的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片classJNI{public:......staticjclassFindClass(JNIEnv*env,constchar*name){CHECK_NON_NULL_ARGUMENT(FindClass,name);Runtime*runtime=Runtime::Current();ClassLinker*class_linker=runtime->GetClassLinker();std::stringdescriptor(NormalizeJniClassDescriptor(name));ScopedObjectAccesssoa(env);Class*c=NULL;if(runtime->IsStarted()){ClassLoader*cl=GetClassLoader(soa);c=class_linker->FindClass(descriptor.c_str(),cl);}else{c=class_linker->FindSystemClass(descriptor.c_str());}returnsoa.AddLocalReference<jclass>(c);}......};這個(gè)函數(shù)定義在文件 art/runtime/jni_internal.cc 中。在ART虛擬機(jī)進(jìn)程中,存在著一個(gè)Runtime單例,用來描述ART運(yùn)行時(shí)。通過調(diào)用Runtime類的靜態(tài)成員函數(shù)Current可以獲得上述Runtime單例。獲得了這個(gè)單例之后,就可以調(diào)用它的成員函數(shù)GetClassLinker來獲得一個(gè)ClassLinker對象。從前面一文可以知道。上述ClassLinker對象是在創(chuàng)建ART虛擬機(jī)的過程中創(chuàng)建的,用來加載類以及鏈接類方法。JNI類的靜態(tài)成員函數(shù) FindClass首先是判斷 ART運(yùn)行時(shí)是否已經(jīng)啟動(dòng)起來。如果已經(jīng)啟動(dòng),那么就通過調(diào)用函數(shù) GetClassLoader來獲得當(dāng)前線程所關(guān)聯(lián)的 ClassLoader,并且以此為參數(shù),調(diào)用前面獲得的 ClassLinker對象的成員函數(shù) FindClass來加載由參數(shù) name指定的類。一般來說,當(dāng)前線程所關(guān)聯(lián)的 ClassLoader就是當(dāng)前正在執(zhí)行的類方法所關(guān)聯(lián)的ClassLoader,即用來加載當(dāng)前正在執(zhí)行的類的 ClassLoader。如果ART虛擬機(jī)還沒有開始執(zhí)行類方法,就像我們現(xiàn)在這個(gè)場景,那么當(dāng)前線程所關(guān)聯(lián)的 ClassLoader實(shí)際上就系統(tǒng)類加載器,即 SystemClassLoader。如果ART運(yùn)行時(shí)還沒有啟動(dòng),那么這時(shí)候只可以加載系統(tǒng)類。這個(gè)通過前面獲得的ClassLinker對象的成員函數(shù)FindSystemClass來實(shí)現(xiàn)的。在我們這個(gè)場景中,ART運(yùn)行時(shí)已經(jīng)啟動(dòng),因此,接下來我們就繼續(xù)分析 ClassLinker類的成員函數(shù) FindClass的實(shí)現(xiàn)。ClassLinker類的成員函數(shù) FindClass的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片mirror::Class*ClassLinker::FindClass(constchar*descriptor,mirror::ClassLoader*class_loader){......Thread*self=Thread::Current();......//Findtheclassintheloadedclassestable.mirror::Class*klass=LookupClass(descriptor,class_loader);if(klass!=NULL){returnEnsureResolved(self,klass);}//Classisnotyetloaded.if(descriptor[0]=='['){......}elseif(class_loader==NULL){DexFile::ClassPathEntrypair=DexFile::FindInClassPath(descriptor,boot_class_path_);if(pair.second!=NULL){returnDefineClass(descriptor,NULL,*pair.first,*pair.second);}}elseif(Runtime::Current()->UseCompileTimeClassPath()){......}else{ScopedObjectAccessUncheckedsoa(self->GetJniEnv());ScopedLocalRef<jobject>class_loader_object(soa.Env(),soa.AddLocalReference<jobject>(class_loader));std::stringclass_name_string(DescriptorToDot(descriptor));ScopedLocalRef<jobject>result(soa.Env(),NULL);{ScopedThreadStateChangetsc(self,kNative);ScopedLocalRef<jobject>class_name_object(soa.Env(),soa.Env()->NewStringUTF(class_name_string.c_str()));if(class_name_object.get()==NULL){returnNULL;}CHECK(class_loader_object.get()!=NULL);result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),WellKnownClasses::java_lang_ClassLoader_loadClass,class_name_object.get()));}if(soa.Self()->IsExceptionPending()){IftheClassLoaderthrew,passthatexceptionup.returnNULL;}elseif(result.get()==NULL){brokenloader-throwNPEtobecompatiblewithDalvikThrowNullPointerException(NULL,StringPrintf("ClassLoader.loadClassfor%s",

returned nullclass_name_string.c_str()).c_str());returnNULL;}else{//success,returnmirror::Class*returnsoa.Decode<mirror::Class*>(result.get());}}ThrowNoClassDefFoundError("Class%snotfound",PrintableString(descriptor).c_str());returnNULL;}這個(gè)函數(shù)定義在文件 art/runtime/class_linker.cc中。參數(shù)descriptor指向的是要加載的類的簽名,而參數(shù)載器,我們假設(shè)它的值不為空,并且指向系統(tǒng)類加載器。

class_loader指向的是一個(gè)類加ClassLinker類的成員函數(shù)FindClass首先是調(diào)用另外一個(gè)成員函數(shù)LookupClass來檢查參數(shù)descriptor指定的類是否已經(jīng)被加載過。如果是的話,那么ClassLinker類的成員函數(shù)LookupClass

就會返回一個(gè)對應(yīng)的

Class對象,這個(gè)

Class對象接著就會返回給調(diào)用者,

表示加載已經(jīng)完成。如果參數(shù)descriptor指定的類還沒有被加載過,這時(shí)候主要就是要看參數(shù)class_loader的值了。如果參數(shù)class_loader的值等于NULL,那么就需要調(diào)用DexFile類的靜態(tài)FindInClassPath來在系統(tǒng)啟動(dòng)類路徑尋找對應(yīng)的類。一旦尋找到,那么就會獲得包含目標(biāo)類的DEX文件,因此接下來就調(diào)用ClassLinker類的另外一個(gè)成員函數(shù)DefineClass從獲得的DEX文件中加載參數(shù)descriptor指定的類了。如果參數(shù)class_loader的值不等于NULL,也就是說ClassLinker類的成員函數(shù)FindClass的調(diào)用者指定了類加載器,那么就通過該類加載器來加載參數(shù)descriptor指定的類。每一個(gè)類加載器在Java層都對應(yīng)有一個(gè)java.lang.ClassLoader對象。通過調(diào)用這個(gè)類的成員函數(shù) loadClass即可加載指定的類。在我們這個(gè)場景中,上述的類是一個(gè)系統(tǒng)類加載器,它負(fù)責(zé)加載系統(tǒng)類。而我們當(dāng)前要加載的類為,它屬于一個(gè)系統(tǒng)類。系統(tǒng)類加載器在加載系統(tǒng)類實(shí)際上也是通過 JNI方法調(diào)用ClassLinker類的成員函數(shù)FindClass來實(shí)現(xiàn)的。只不過這時(shí)候傳進(jìn)來的參數(shù) class_loader是一個(gè) NULL 值。這樣,ClassLinker類的成員函數(shù) FindClass就會在系統(tǒng)啟動(dòng)類路徑中尋找參數(shù) descriptor指定的類可以在哪一個(gè) DEX文件加載,這是通過調(diào)用 DexFile類的靜態(tài)成員函數(shù) FindInClassPath來實(shí)現(xiàn)的。所謂的系統(tǒng)啟動(dòng)類路徑,其實(shí)就是一系列指定的由系統(tǒng)提供的DEX文件,這些DEX文件保存在ClassLinker類的成員變量boot_class_path_描述的一個(gè)向量中。那么問題就來了,這些DEX文件是怎么來的呢?我們知道,在ART運(yùn)行時(shí)中,我們使用的是OAT文件。如果看過前面這篇文章,就會很容易知道,OAT文件里面包含有DEX文件。而且ART運(yùn)行時(shí)在啟動(dòng)的時(shí)候,會加載一個(gè)名稱為system@framework@boot.art@classes.oat的OAT文件。這個(gè)OAT文件包含有多個(gè)DEX文件,每一個(gè)DEX文件都是一個(gè)系統(tǒng)啟動(dòng)類路徑,它們會被添加到ClassLinker類的成員變量boot_class_path_描述的向量中去。這里調(diào)用 DexFile 類的靜態(tài)成員函數(shù) FindInClassPath,實(shí)際要完成的工作就是從ClassLinker類的成員變量 boot_class_path_描述的一系列的 DEX文件中檢查哪一個(gè) DEX

文件包含有參數(shù) descriptor

指定的類。這可以通過解析

DEX

文件來實(shí)現(xiàn),關(guān)于

DEX

文件的格式,可以參考官方文檔:

。知道了參數(shù) descriptor指定的類定義在哪一個(gè) DEX 文件之后,就可以通過ClassLinker類的另外一個(gè)成員函數(shù) DefineClass來從中加載它了。接下來,我們就繼續(xù)分析ClassLinker類的成員函數(shù) DefineClass的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片mirror::Class*ClassLinker::DefineClass(constchar*descriptor,mirror::ClassLoader*class_loader,constDexFile&dex_file,constDexFile::ClassDef&dex_class_def){Thread*self=Thread::Current();SirtRef<mirror::Class>klass(self,NULL);Loadtheclassfromthedexfile.if(UNLIKELY(!init_done_)){finishupinitofhandcraftedclass_roots_if(strcmp(descriptor,"Ljava/lang/Object;")==0){klass.reset(GetClassRoot(kJavaLangObject));}elseif(strcmp(descriptor,"Ljava/lang/Class;")==0){klass.reset(GetClassRoot(kJavaLangClass));}elseif(strcmp(descriptor,"Ljava/lang/String;")==0){klass.reset(GetClassRoot(kJavaLangString));}elseif(strcmp(descriptor,"Ljava/lang/DexCache;")==0){klass.reset(GetClassRoot(kJavaLangDexCache));}elseif(strcmp(descriptor,"Ljava/lang/reflect/ArtField;")==0){klass.reset(GetClassRoot(kJavaLangReflectArtField));}elseif(strcmp(descriptor,"Ljava/lang/reflect/ArtMethod;")==0){klass.reset(GetClassRoot(kJavaLangReflectArtMethod));}else{klass.reset(AllocClass(self,SizeOfClass(dex_file,dex_class_def)));}}else{klass.reset(AllocClass(self,SizeOfClass(dex_file,dex_class_def)));}......LoadClass(dex_file,dex_class_def,klass,class_loader);......{//Addthenewlyloadedclasstotheloadedclassestable.mirror::Class*existing=InsertClass(descriptor,klass.get(),Hash(descriptor));if(existing!=NULL){//Wefailedtoinsertbecauseweracedwithanotherthread.CallingEnsureResolvedmaycause//thisthreadtoblock.returnEnsureResolved(self,existing);}}......if(!LinkClass(klass,NULL,self)){//Linkingfailed.klass->SetStatus(mirror::Class::kStatusError,self);returnNULL;}......returnklass.get();}這個(gè)函數(shù)定義在文件

art/runtime/class_linker.cc

中。ClassLinker

類有一個(gè)類型為

bool

的成員變量

init_done_,用來表示

ClassLinker

是否已經(jīng)初始化完成。 ClassLinker在創(chuàng)建的時(shí)候,有一個(gè)初始化過程,用來創(chuàng)建一些內(nèi)部類。這些內(nèi)部類要么是手動(dòng)創(chuàng)建的,要么是從Image空間獲得的。關(guān)于ART虛擬機(jī)的Image空間,我們在后面分析ART垃圾收集機(jī)制的文章中再詳細(xì)分析。調(diào)用ClassLinker類的成員函數(shù) DefineClass的時(shí)候,如果 ClassLinker正處于初始化過程,即其成員變量 init_done_的值等于 false,并且參數(shù) descriptor描述的是特定的內(nèi)部類,那么就將本地變量 klass指向它們,其余情況則會通過成員函數(shù) AllocClass為其分配存儲空間,以便后面通過成員函數(shù) LoadClass進(jìn)行初始化。ClassLinker類的成員函數(shù) LoadClass用來從指定的 DEX文件中加載指定的類。指定的類從DEX文件中加載完成后,需要通過另外一個(gè)成員函數(shù) InsertClass添加到ClassLinker的已加載類列表中去。 如果指定的類之前已經(jīng)加載過, 即調(diào)用成員函數(shù) InsertClass得到的返回值不等于空,那么就說明有另外的一個(gè)線程也正在加載指定的類。這時(shí)候就需要調(diào)用成員函數(shù)EnsureResolved來保證(等待)該類已經(jīng)加載并且解析完成。另一方面,如果沒有其它線程加載指定的類,那么當(dāng)前線程從指定的DEX文件加載完成指定的類后,還需要調(diào)用成員函數(shù)LinkClass來對加載后的類進(jìn)行解析。最后,一個(gè)類型為Class的對象就可以返回給調(diào)用者了,用來表示一個(gè)已經(jīng)加載和解析完成的類。接下來,我們主要分析

ClassLinker

類的成員函數(shù)

LoadClass

的實(shí)現(xiàn),以便可以了解類的加載過程。ClassLinker類的成員函數(shù) LoadClass的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidClassLinker::LoadClass(constDexFile&dex_file,constDexFile::ClassDef&dex_class_def,SirtRef<mirror::Class>&klass,mirror::ClassLoader*class_loader){......klass->SetClassLoader(class_loader);......klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));.....//Loadfieldsfields.constbyte*class_data=dex_file.GetClassData(dex_class_def);......ClassDataItemIteratorit(dex_file,class_data);Thread*self=Thread::Current();mirror::ObjectArray<mirror::ArtField>*

statics

=

AllocArtFieldArray(self,it.NumStaticFields());......klass->SetSFields(statics);}if(it.NumInstanceFields()!=0){mirror::ObjectArray<mirror::ArtField>*fields=AllocArtFieldArray(self,it.NumInstanceFields());......klass->SetIFields(fields);}for(size_ti=0;it.HasNextStaticField();i++,it.Next()){SirtRef<mirror::ArtField>sfield(self,AllocArtField(self));......klass->SetStaticField(i,sfield.get());LoadField(dex_file,it,klass,sfield);}for(size_ti=0;it.HasNextInstanceField();i++,it.Next()){SirtRef<mirror::ArtField>ifield(self,AllocArtField(self));......klass->SetInstanceField(i,ifield.get());LoadField(dex_file,it,klass,ifield);}UniquePtr<constOatFile::OatClass>oat_class;if(Runtime::Current()->IsStarted()&&!Runtime::Current()->UseCompileTimeClassPath()){oat_class.reset(GetOatClass(dex_file,klass->GetDexClassDefIndex()));}//Loadmethods.if(it.NumDirectMethods()!=0){//TODO:appenddirectmethodstoclassobjectmirror::ObjectArray<mirror::ArtMethod>*directs=AllocArtMethodArray(self,it.NumDirectMethods());......klass->SetDirectMethods(directs);}if(it.NumVirtualMethods()!=0){TODO:appenddirectmethodstoclassobjectmirror::ObjectArray<mirror::ArtMethod>*virtuals=AllocArtMethodArray(self,it.NumVirtualMethods());......klass->SetVirtualMethods(virtuals);}size_tclass_def_method_index=0;for(size_ti=0;it.HasNextDirectMethod();i++,it.Next()){SirtRef<mirror::ArtMethod>method(self,LoadMethod(self,dex_file,it,klass));......klass->SetDirectMethod(i,method.get());if(oat_class.get()!=NULL){LinkCode(method,oat_class.get(),class_def_method_index);}method->SetMethodIndex(class_def_method_index);class_def_method_index++;}for(size_ti=0;it.HasNextVirtualMethod();i++,it.Next()){SirtRef<mirror::ArtMethod>method(self,LoadMethod(self,dex_file,it,klass));......klass->SetVirtualMethod(i,method.get());......if(oat_class.get()!=NULL){LinkCode(method,oat_class.get(),class_def_method_index);}class_def_method_index++;}......}這個(gè)函數(shù)定義在文件 art/runtime/class_linker.cc中。我們首先要明確一下各個(gè)參數(shù)的含義:dex_file: 類型為DexFile,描述要加載的類所在的 DEX文件。dex_class_def:類型為ClassDef,描述要加載的類在 DEX文件里面的信息。klass:類型為Class,描述加載完成的類。class_loader: 類型為ClassLoader,描述所使用的類加載器??偟膩碚f,ClassLinker類的成員函數(shù)LoadClassdex_class_def、class_loader三個(gè)參數(shù)包含的相關(guān)信息設(shè)置到參數(shù)以便可以得到一個(gè)完整的已加載類信息。

的任務(wù)就是要用dex_file、klass描述的Class對象去,ClassLinker類的成員函數(shù) LoadClass主要完成的工作如下所示:將參數(shù)class_loader描述的ClassLoader設(shè)置到klass描述的Class對象中去,即給每一個(gè)已加載類關(guān)聯(lián)一個(gè)類加載器。通過DexFile類的成員函數(shù)GetIndexForClassDef獲得正在加載的類在DEX文件中的類索引號,并且設(shè)置到klass描述的Class對象中去。這個(gè)類索引號是一個(gè)很重要的信息,因?yàn)槲覀冃枰ㄟ^類索引號在相應(yīng)的OAT文件找到一個(gè)OatClass結(jié)構(gòu)體。有了這個(gè)OatClass結(jié)構(gòu)體之后,我們才可以找到類方法對應(yīng)的本地機(jī)器指令。具體可以參考前面圖 1和一文。從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的靜態(tài)成員變量和實(shí)例成員變量個(gè)數(shù),并且為每一個(gè)靜態(tài)成員變量和實(shí)例成員變量都分配一個(gè) ArtField對象,接著通過ClassLinker類的成員函數(shù)LoadField對這些ArtField對象進(jìn)行初始化。初始好得到的ArtField對象全部保存在 klass描述的Class對象中。4.調(diào)用ClassLinker類的成員函數(shù) GetOatClass,從相應(yīng)的 OAT文件中找到與正在加載的類對應(yīng)的一個(gè) OatClass結(jié)構(gòu)體oat_class。這需要利用到上面提到的 DEX類索引號,這是因?yàn)镈EX類和OAT類根據(jù)索引號存在一一對應(yīng)關(guān)系。這一點(diǎn)可以參考圖 1和一文。從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的直接成員函數(shù)和虛擬成員函數(shù)個(gè)數(shù),并且為每一個(gè)直接成員函數(shù)和虛擬成員函數(shù)都分配一個(gè) ArtMethod對象,接著通過ClassLinker類的成員函數(shù)LoadMethod對這些ArtMethod對象進(jìn)行初始化。初始好得到的ArtMethod對象全部保存在klass描述的Class對象中。每一個(gè)直接成員函數(shù)和虛擬成員函數(shù)都對應(yīng)有一個(gè)函數(shù)索引號。根據(jù)這個(gè)函數(shù)索引號可以在第4步得到的OatClass結(jié)構(gòu)體中找到對應(yīng)的本地機(jī)器指令,具體可以參考前面圖1和一文。所有與這些成員函數(shù)關(guān)聯(lián)的本地機(jī)器指令信息通過全局函數(shù) LinkCode設(shè)置到klass描述的Class對象中。總結(jié)來說,參數(shù) klass描述的Class對象包含了一系列的 ArtField對象和ArtMethod對象,其中,ArtField對象用來描述成員變量信息,而 ArtMethod用來描述成員函數(shù)信息。接下來,我們繼續(xù)分析全局函數(shù) LinkCode的實(shí)現(xiàn),以便可以了解如何在一個(gè) OAT文件中找到一個(gè) DEX類方法的本地機(jī)器指令。函數(shù)LinkCode的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片staticvoidLinkCode(SirtRef<mirror::ArtMethod>&method,constOatFile::OatClass*oat_class,uint32_tmethod_index)SHARED_LOCKS_REQUIRED(Locks::mutator_lock_){//Methodshouldn'thavealreadybeenlinked.DCHECK(method->GetEntryPointFromCompiledCode()==NULL);Everykindofmethodshouldatleastgetaninvokestubfromtheoat_method.non-abstractmethodsalsogettheircodepointers.constOatFile::OatMethodoat_method=oat_class->GetOatMethod(method_index);oat_method.LinkMethod(method.get());Installentrypointfrominterpreter.Runtime*runtime=Runtime::Current();boolenter_interpreter=NeedsInterpreter(method.get(),method->GetEntryPointFromCompiledCode());if(enter_interpreter){method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);}else{method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);}if(method->IsAbstract()){method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());return;}if(method->IsStatic()&&!method->IsConstructor()){Forstaticmethodsexcludingtheclassinitializer,installthetrampoline.ItwillbereplacedbytheproperentrypointbyClassLinker::FixupStaticTrampolinesafterinitializingclass(seeClassLinker::InitializeClassmethod).method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));}elseif(enter_interpreter){Setentrypointfromcompiledcodeifthere'snocodeorininterpreteronlymode.method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());}if(method->IsNative()){Unregisteringrestoresthedlsymlookupstub.method->UnregisterNative(Thread::Current());}//Allowinstrumentationitschancetohijackcode.runtime->GetInstrumentation()->UpdateMethodsCode(method.get(),method->GetEntryPointFromCompiledCode());}這個(gè)函數(shù)定義在文件art/runtime/class_linker.cc中。參數(shù)method表示要設(shè)置本地機(jī)器指令的類方法,參數(shù)

oat_class表示類方法 method在OAT文件中對應(yīng)的OatClass結(jié)構(gòu)體,參數(shù)method_index表示類方法method的索引號。通過參數(shù)method_index描述的索引號可以在 oat_class表示的OatClass結(jié)構(gòu)體中找到一個(gè)OatMethod結(jié)構(gòu)體oat_method。這個(gè)OatMethod結(jié)構(gòu)描述了類方法 method的本地機(jī)器指令相關(guān)信息,通過調(diào)用它的成員函數(shù)

LinkMethod

可以將這些信息設(shè)置到參數(shù) method描述的ArtMethod對象中去。如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片constvoid*OatFile::OatMethod::GetCode()const{returnGetOatPointer<constvoid*>(code_offset_);}......voidOatFile::OatMethod::LinkMethod(mirror::ArtMethod*method)const{CHECK(method!=NULL);method->SetEntryPointFromCompiledCode(GetCode());method->SetFrameSizeInBytes(frame_size_in_bytes_);method->SetCoreSpillMask(core_spill_mask_);method->SetFpSpillMask(fp_spill_mask_);method->SetMappingTable(GetMappingTable());method->SetVmapTable(GetVmapTable());method->SetNativeGcMap(GetNativeGcMap()); //UsedbynativemethodsinworkaroundJNImode.}中 的

這個(gè)函數(shù)定義在文件art/runtime/oat_file.cc中。其中,最重要的就是通過OatMethod類的成員函數(shù)字段,并且通過調(diào)用code_offset_

GetCode獲得OatMethod結(jié)構(gòu)體ArtMethod 類 的 成 員 函 數(shù)SetEntryPointFromCompiledCode設(shè)置到參數(shù)method描述的ArtMethod對象中去。OatMethod結(jié)構(gòu)體中的code_offset_字段指向的是一個(gè)本地機(jī)器指令函數(shù),這個(gè)本地機(jī)器指令函數(shù)正是通過翻譯參數(shù) method描述的類方法的 DEX字節(jié)碼得到的。回到函數(shù) LinkCode 中,它接著調(diào)用另外一個(gè)全局函數(shù) NeedsInterpreter檢查參數(shù)method描述的類方法是否需要通過解釋器執(zhí)行,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片//Returnstrueifthemethodmustrunwithinterpreter,falseotherwise.staticboolNeedsInterpreter(constmirror::ArtMethod*method,constvoid*code){if(code==NULL){//Nocode:needinterpreter.returntrue;}......Ifinterpretermodeisenabled,everymethod(exceptnativeandproxy)mustberunwithinterpreter.returnRuntime::Current()->GetInstrumentation()->InterpretOnly()&&!method->IsNative()&&!method->IsProxyMethod();}這個(gè)函數(shù)定義在文件 art/runtime/class_linker.cc中。在以下兩種情況下,一個(gè)類方法需要通過解釋器來執(zhí)行:1.沒有對應(yīng)的本地機(jī)器指令,即參數(shù) code的值等于 NULL。ART虛擬機(jī)運(yùn)行在解釋模式中,并且類方法不是JNI方法,并且也不是代理方法。調(diào)用Runtime類的靜態(tài)成員函數(shù) Current獲得的是描述 ART運(yùn)行時(shí)的一個(gè) Runtime對象。調(diào)用這個(gè) Runtime對象的成員函數(shù) GetInstrumentation獲得的是一個(gè) Instrumentation對象。這個(gè) Instrumentation 對象是用來調(diào)試 ART 運(yùn)行時(shí)的,通過調(diào)用它的成員函數(shù)InterpretOnly可以知道 ART虛擬機(jī)是否運(yùn)行在解釋模式中。中,JNI

因?yàn)镴NI方法是沒有對應(yīng)的 DEX字節(jié)碼的,因此即使 ART虛擬機(jī)運(yùn)行在解釋模式方法也不能通過解釋器來執(zhí)行。至于代理方法,由于是動(dòng)態(tài)生成的(沒有對應(yīng)的DEX

字節(jié)碼),因此即使

ART

虛擬機(jī)運(yùn)行在解釋模式中,它們也不通過解釋器來執(zhí)行(這一點(diǎn)猜測的,還沒有確認(rèn))

。回到函數(shù) LinkCode中,如果調(diào)用函數(shù) NeedsInterpreter得到的返回值 enter_interpreter等于true,那么就意味著參數(shù) method描述的類方法需要通過解釋器來執(zhí)行,這時(shí)候就將函數(shù)artInterpreterToInterpreterBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。否則的話,就將另外一個(gè)函數(shù)artInterpreterToCompiledCodeBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。為什么我們需要為類方法設(shè)置解釋器入口點(diǎn)呢?根據(jù)前面的分析可以知道, 在ART虛擬機(jī)中,并不是所有的類方法都是有對應(yīng)的本地機(jī)器指令的, 并且即使一個(gè)類方法有對應(yīng)的本地機(jī)器指令,當(dāng) ART虛擬機(jī)以解釋模式運(yùn)行時(shí),它也需要通過解釋器來執(zhí)行。當(dāng)以解釋器執(zhí)行的類方法在執(zhí)行的過程中調(diào)用了其它的類方法時(shí), 解釋器就需要進(jìn)一步知道被調(diào)用的類方法是應(yīng)用以解釋方式執(zhí)行, 還是本地機(jī)器指令方法執(zhí)行。 為了能夠進(jìn)行統(tǒng)一處理, 就給每一個(gè)類方法都設(shè)置一個(gè)解釋器入口點(diǎn)。 需要通過解釋執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToInterpreterBridge,它會繼續(xù)通過解釋器來執(zhí)行該類方法。需要通過本地機(jī)器指令執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToCompiledCodeBridge,它會間接地調(diào)用該類方法的本地機(jī)器指令。函數(shù)LinkCode繼續(xù)往下執(zhí)行,判斷參數(shù)method描述的類方法是否是一個(gè)抽象方法。抽象方法聲明類中是沒有實(shí)現(xiàn)的,必須要由子類實(shí)現(xiàn)。因此抽象方法在聲明類中是沒有對應(yīng)的本地機(jī)器指令的,它們必須要通過解釋器來執(zhí)行。不過,為了能夠進(jìn)行統(tǒng)一處理,我們?nèi)匀患傺b抽象方法有對應(yīng)的本地機(jī)器指令函數(shù),只不過這個(gè)本地機(jī)器指令函數(shù)被設(shè)置為GetCompiledCodeToInterpreterBridge。當(dāng)函數(shù)GetCompiledCodeToInterpreterBridge被調(diào)用時(shí),就會自動(dòng)進(jìn)入到解釋器中去。對于非抽象方法,函數(shù) LinkCode還要繼續(xù)往下處理。到這里有一點(diǎn)是需要注意的,前面通過調(diào)用 OatMethod類的成員函數(shù) LinkMethod,我們已經(jīng)設(shè)置好參數(shù) method描述的類方法的本地機(jī)器指令了。但是,在以下兩種情況下,我們需要進(jìn)行調(diào)整:1.當(dāng)參數(shù)method描述的類方法是一個(gè)非類靜態(tài)初始化函數(shù)(classinitializer)的靜態(tài)方法時(shí),我們不能直接執(zhí)行翻譯其DEX字節(jié)碼得到的本地機(jī)器指令。這是因?yàn)轭愳o態(tài)方法可以在不創(chuàng)建類對象的前提下執(zhí)行。這意味著一個(gè)類靜態(tài)方法在執(zhí)行的時(shí)候,對應(yīng)的類可能還沒有初始化好。這時(shí)候我們就需要先將對應(yīng)的類初始化好,再執(zhí)行相應(yīng)的靜態(tài)方法。為了能夠做到這一點(diǎn)。我們就先調(diào)用GetResolutionTrampoline函數(shù)得到一個(gè)Tampoline函數(shù),接著將這個(gè)Trampoline函數(shù)作為靜態(tài)方法的本地機(jī)器指令。這樣如果類靜態(tài)方法在對應(yīng)的類初始化前被調(diào)用,就會觸發(fā)上述的Trampoline函數(shù)被執(zhí)行。而當(dāng)上述Trampoline函數(shù)執(zhí)行時(shí),它們先初始化好對應(yīng)的類,再調(diào)用原來的類靜態(tài)方法對應(yīng)的本地機(jī)器指令。按照代碼中的注釋,當(dāng)一個(gè)類初始化完成之后,就可以調(diào)用函數(shù)ClassLinker::FixupStaticTrampolines來修復(fù)該類的靜態(tài)成員函數(shù)的本地機(jī)器指令,也是通過翻譯DEX字節(jié)碼得到的本地機(jī)器指令。這里需要注意的是,為什么類靜態(tài)初始化函數(shù)不需要按照其它的類靜態(tài)方法一樣設(shè)置Tampoline函數(shù)呢?這是因?yàn)轭愳o態(tài)初始化函數(shù)是一定保證是在類初始化過程中執(zhí)行的。當(dāng)參數(shù)method描述的類方法需要通過解釋器執(zhí)行時(shí),那么當(dāng)該類方法執(zhí)行時(shí),就不能執(zhí)行它的本地機(jī)器指令,因此我們就先調(diào)用GetCompiledCodeToInterpreterBridge函數(shù)獲得一個(gè)橋接函數(shù),并且將這個(gè)橋接函數(shù)假裝為類方法的本地機(jī)器指令。一旦該橋接函數(shù)被執(zhí)行,它就會入到解釋器去執(zhí)行類方法。通過這種方式,我們就可以以統(tǒng)一的方法來調(diào)用解釋執(zhí)行和本地機(jī)器指令執(zhí)行的類方法。函數(shù)LinkCode接下來繼續(xù)判斷參數(shù) method描述的類方法是否是一個(gè) JNI方法。如果是的話,那么就調(diào)用 ArtMethod 類的成員函數(shù) UnregisterNative來初始化它的 JNI方法調(diào)用接口。ArtMethod類的成員函數(shù) UnregisterNative的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidArtMethod::UnregisterNative(Thread*self){CHECK(IsNative())<<PrettyMethod(this);restorestubtolookupnativepointerviadlsymRegisterNative(self,GetJniDlsymLookupStub());}這個(gè)函數(shù)定義在文件 runtime/mirror/art_method.cc 中。ArtMethod類的成員函數(shù) UnregisterNative實(shí)際上就是將一個(gè) JNI方法的初始化入口設(shè)置為通過調(diào)用函數(shù) GetJniDlsymLookupStub 獲得的一個(gè) Stub。這個(gè) Stub的作用是,當(dāng)一個(gè)JNI方法被調(diào)用時(shí),如果還沒有顯示地注冊有 Native函數(shù),那么它就會自動(dòng)從已加載的SO文件查找是否存在一個(gè)對應(yīng)的 Native函數(shù)。如果存在的話,就將它注冊為 JNI方法的Native函數(shù),并且執(zhí)行它。這就是隱式的 JNI方法注冊。回到函數(shù) LinkCode,它最后調(diào)用 Instrumentation類的成員函數(shù) UpdateMethodsCode檢查是否要進(jìn)一步修改參數(shù) method描述的類方法的本地機(jī)器指令入口, 它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy 在CODE上查看代碼片派生到我的代碼片voidInstrumentation::UpdateMethodsCode(mirror::ArtMethod*method,constvoid*code)const{if(LIKELY(!instrumentation_stubs_installed_)){method->SetEntryPointFromCompiledCode(code);}else{if(!interpreter_stubs_installed_||method->IsNative()){method->SetEntryPointFromCompiledCode(GetQuickInstrumentationEntryPoint());}else{method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());}}}這個(gè)函數(shù)定義在文件 art/runtime/instrumentation.cc 中。Instrumentation類是用來調(diào)用 ART運(yùn)行時(shí)的。例如,當(dāng)我們需要監(jiān)控類方法的調(diào)用時(shí),就可以往 Instrumentation注冊一些 Listener。這樣當(dāng)類方法調(diào)用時(shí),這些注冊的 Listener就會得到回調(diào)。當(dāng) Instrumentation 注冊有相應(yīng)的 Listener 時(shí),它的成員變量instrumentation_stubs_installed_的值就會等于

true。因此,當(dāng)Instrumentation類的成員變量 instrumentation_stubs_installed_的值等于 true時(shí),我們需要使用一個(gè)監(jiān)控函數(shù)來替換掉類方法原來的本地機(jī)器指令。 這樣當(dāng)類方法被調(diào)用時(shí),監(jiān)控函數(shù)就獲得控制權(quán),它可以在調(diào)用原來的本地機(jī)器指令前后,向注冊的 Listener發(fā)出通知。對于JNI方法,我們通過調(diào)用函數(shù) GetQuickInstrumentationEntryPoint 獲得的函數(shù)作為其監(jiān)控函數(shù);而對其它的類方法,我們通過調(diào)用函數(shù) GetCompiledCodeToInterpreterBridge獲得的函數(shù)作為其監(jiān)控函數(shù)。另一方面,如果沒有 Listener 注冊到 Instrumentation 中,即它的成員變量instrumentation_stubs_installed_的值等于 false,那么 Instrumentation 類的成員函UpdateMethodsCode就會使用參數(shù) code描述的本地機(jī)器指令作為參數(shù) method描述的類方法的本地機(jī)器指令入口。 參數(shù)code描述的本地機(jī)器指一般就是翻譯類方法的 DEX字節(jié)碼得到的本地機(jī)器指令了。實(shí)際上是相當(dāng)于沒有修改類方法的本地機(jī)器指令入口。這樣,一個(gè)類的加載過程就完成了。加載完成后,得到的是一個(gè) Class對象。這個(gè)Class對象關(guān)聯(lián)有一系列的 ArtField對象和ArtMethod對象。其中,ArtField對象描述的是成員變量,而ArtMethod對象描述的是成員函數(shù)。對于每一個(gè)ArtMethod對象,它都有一個(gè)解釋器入口點(diǎn)和一個(gè)本地機(jī)器指令入口點(diǎn)。這樣,無論一個(gè)類方法是通過解釋器執(zhí)行,還是直接以本地機(jī)器指令執(zhí)行,我們都可以以統(tǒng)一的方式來進(jìn)行調(diào)用。同時(shí),理解了上述的類加載過程后,我們就可以知道,我們在Native層通過JNI接口FindClass查找或者加載類時(shí),得到的一個(gè)不透明的jclass值,實(shí)際上

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論