【Android開發(fā)高級系列】Dalvik虛擬機專題

一,apk以進程的形式運行售睹,進程的創(chuàng)建是由zygote越锈。

參考文章《深入理解Dalvik虛擬機- Android應用進程啟動過程分析》

二仗嗦,進程運行起來之后,初始化JavaVM

參考文章《深入理解ART虛擬機—虛擬機的啟動》

三甘凭,JavaVM創(chuàng)建之后稀拐,我們就有了JNINativeInterface,里面包含了所有的Java接口对蒲,比如FindClass钩蚊,NewObject,CallObjectMethod等

參考文章《深入理解ART虛擬機—虛擬機的啟動》

四蹈矮,Java的運行時的功能簡單來說分為:類的加載和函數(shù)Method的執(zhí)行

參考文章《深入理解Dalvik虛擬機- 解釋器的運行機制》


1 虛擬機機制

1.1 Java虛擬機

java virtual machine(java虛擬機的運行機制)

http://webzixue.iteye.com/blog/1227802

????????Java語言使用模式 ????Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行砰逻。Java虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行。

????????Java虛擬機體系結(jié)構(gòu)  Java虛擬機由五個部分組成:一組指令集泛鸟、一組寄存器、一個、一個無用單元收集堆(Garbage-collected-heap)贱勃、一個方法區(qū)域恬汁。這五部分是Java虛擬機的邏輯成份,不依賴任何實現(xiàn)技術(shù)或組織方式,但它們的功能必須在真實機器上以某種方式實現(xiàn)。?

1.1.1 Java指令集

  Java虛擬機支持大約248個字節(jié)碼再芋。每個字節(jié)碼執(zhí)行一種基本的CPU運算菊霜,例如,把一個整數(shù)加到寄存器济赎、子程序轉(zhuǎn)移等鉴逞。Java指令集相當于Java程序的匯編語言。?

  Java指令集中的指令包含一個單字節(jié)的操作符司训,用于指定要執(zhí)行的操作构捡,還有0個或多個操作數(shù),提供操作所需的參數(shù)或數(shù)據(jù)壳猜。許多指令沒有操作數(shù)勾徽,僅由一個單字節(jié)的操作符構(gòu)成。?

  虛擬機的內(nèi)層循環(huán)的執(zhí)行過程如下:?

  do{?

  ????取一個操作符字節(jié);?

????  根據(jù)操作符的值執(zhí)行一個動作;?

  }while(程序未結(jié)束)?

  由于指令系統(tǒng)的簡單性统扳,使得虛擬機執(zhí)行的過程十分簡單喘帚,從而有利于提高執(zhí)行的效率。指令中操作數(shù)的數(shù)量和大小是由操作符決定的闪幽。如果操作數(shù)比一個字節(jié)大啥辨,那么它存儲的順序是高位字節(jié)優(yōu)先。例如盯腌,一個16位的參數(shù)存放時占用兩個字節(jié)溉知,其值為:?

  第一個字節(jié)*256+第二個字節(jié)字節(jié)碼指令流一般只是字節(jié)對齊的。指令tabltch和lookup是例外腕够,在這兩條指令內(nèi)部要求強制的4字節(jié)邊界對齊级乍。?

1.1.2 寄存器

  Java虛擬機的寄存器用于保存機器的運行狀態(tài),與微處理器中的某些專用寄存器類似帚湘。 Java虛擬機的寄存器有四種:?

  ??pc: Java程序計數(shù)器玫荣。?

  ??optop: 指向操作數(shù)棧頂端的指針。?

  ??frame: 指向當前執(zhí)行方法的執(zhí)行環(huán)境的指針大诸。?

  ??vars: 指向當前執(zhí)行方法的局部變量區(qū)第一個變量的指針捅厂。?

1.1.3 棧

  Java虛擬機的棧有三個區(qū)域:局部變量區(qū)贯卦、運行環(huán)境區(qū)、操作數(shù)區(qū)焙贷。?

  (1)局部變量區(qū)? 每個Java方法使用一個固定大小的局部變量集撵割。它們按照與vars寄存器的字偏移量來尋址。局部變量都是32位的辙芍。長整數(shù)和雙精度浮點數(shù)占據(jù)了兩個局部變量的空間啡彬,卻按照第一個局部變量的索引來尋址。(例如一個具有索引n的局部變量故硅,如果是一個雙精度浮點數(shù)庶灿,那么它實際占據(jù)了索引n和n+1所代表的存儲空間)虛擬機規(guī)范并不要求在局部變量中的64位的值是64位對齊的。虛擬機提供了把局部變量中的值裝載到操作數(shù)棧的指令吃衅,也提供了把操作數(shù)棧中的值寫入局部變量的指令往踢。?

  (2)運行環(huán)境區(qū)? 在運行環(huán)境中包含的信息用于動態(tài)鏈接,正常的方法返回以及異常傳播徘层。?

 ??動態(tài)鏈接?

  運行環(huán)境包括對指向當前類和當前方法的解釋器符號表的指針菲语,用于支持方法代碼的動態(tài)鏈接。方法的class文件代碼在引用要調(diào)用的方法和要訪問的變量時使用符號惑灵。動態(tài)鏈接把符號形式的方法調(diào)用翻譯成實際方法調(diào)用山上,裝載必要的類以解釋還沒有定義的符號,并把變量訪問翻譯成與這些變量運行時的存儲結(jié)構(gòu)相應的偏移地址英支。動態(tài)鏈接方法和變量使得方法中使用的其它類的變化不會影響到本程序的代碼佩憾。?

 ??正常的方法返回?

  如果當前方法正常地結(jié)束了,在執(zhí)行了一條具有正確類型的返回指令時干花,調(diào)用的方法會得到一個返回值妄帘。執(zhí)行環(huán)境在正常返回的情況下用于恢復調(diào)用者的寄存器,并把調(diào)用者的程序計數(shù)器增加一個恰當?shù)臄?shù)值池凄,以跳過已執(zhí)行過的方法調(diào)用指令抡驼,然后在調(diào)用者的執(zhí)行環(huán)境中繼續(xù)執(zhí)行下去。?

 ??異常和錯誤傳播?

  異常情況在Java中被稱作Error(錯誤)或Exception(異常)肿仑,是Throwable類的子類致盟,在程序中的原因是:①動態(tài)鏈接錯,如無法找到所需的class文件尤慰。②運行時錯馏锡,如對一個空指針的引用。

? ????程序使用了throw語句伟端。?

  當異常發(fā)生時杯道,Java虛擬機采取如下措施:

  ??檢查與當前方法相聯(lián)系的catch子句表。每個catch子句包含其有效指令范圍责蝠、能夠處理的異常類型党巾、以及處理異常的代碼塊地址萎庭。?

  ??與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令范圍之內(nèi),發(fā)生的異常類型是其能處理的異常類型的子類型齿拂。如果找到了匹配的catch子句擎椰,那么系統(tǒng)轉(zhuǎn)移到指定的異常處理塊處執(zhí)行;如果沒有找到異常處理塊创肥,重復尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過值朋。?

  ??由于虛擬機從第一個匹配的catch子句處繼續(xù)執(zhí)行叹侄,所以catch子句表中的順序是很重要的。因為Java代碼是結(jié)構(gòu)化的昨登,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中趾代,對任意可能的程序計數(shù)器的值,都可以用線性的順序找到合適的異常處理塊丰辣,以處理在該程序計數(shù)器值下發(fā)生的異常情況撒强。?

  ??如果找不到匹配的catch子句,那么當前方法得到一個"未截獲異常"的結(jié)果并返回到當前方法的調(diào)用者笙什,好像異常剛剛在其調(diào)用者中發(fā)生一樣飘哨。如果在調(diào)用者中仍然沒有找到相應的異常處理塊,那么這種錯誤傳播將被繼續(xù)下去琐凭。如果錯誤被傳播到最頂層芽隆,那么系統(tǒng)將調(diào)用一個缺省的異常處理塊。?

  (3)操作數(shù)棧區(qū)? 機器指令只從操作數(shù)棧中取操作數(shù)统屈,對它們進行操作胚吁,并把結(jié)果返回到棧中。選擇棧結(jié)構(gòu)的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上愁憔,也能夠高效地模擬虛擬機的行為腕扶。操作數(shù)棧是32位的,它用于給方法傳遞參數(shù)吨掌,并從方法接收結(jié)果半抱,也用于支持操作的參數(shù)并保存操作的結(jié)果。例如膜宋,iadd指令將兩個整數(shù)相加代虾,相加的兩個整數(shù)應該是操作數(shù)棧頂?shù)膬蓚€字,這兩個字是由先前的指令壓進堆棧的激蹲。這兩個整數(shù)將從堆棧彈出棉磨、相加,并把結(jié)果壓回到操作數(shù)棧中学辱。?

  每個原始數(shù)據(jù)類型都有專門的指令對它們進行必須的操作乘瓤。每個操作數(shù)在棧中需要一個存儲位置环形,除了long和double型,它們需要兩個位置衙傀。操作數(shù)只能被適用于其類型的操作符所操作抬吟。例如,壓入兩個int類型的數(shù)统抬,如果把它們當作是一個long類型的數(shù)則是非法的火本。在Sun的虛擬機實現(xiàn)中,這個限制由字節(jié)碼驗證器強制實行聪建。但是钙畔,有少數(shù)操作(操作符dupe和swap),用于對運行時數(shù)據(jù)區(qū)進行操作時是不考慮類型的金麸。?

1.1.4 無用單元收集堆

  Java的堆是一個運行時數(shù)據(jù)區(qū)擎析,類的實例(對象)從中分配空間。Java語言具有無用單元收集能力:它不給程序員顯式釋放對象的能力挥下。Java不規(guī)定具體使用的無用單元收集算法揍魂,可以根據(jù)系統(tǒng)的需求使用各種各樣的算法。?

1.1.5 方法區(qū)

  方法區(qū)與傳統(tǒng)語言中的編譯后代碼或是Unix進程中的正文段類似棚瘟。它保存方法代碼(編譯后的java代碼)和符號表现斋。在當前的Java實現(xiàn)中,方法代碼不包括在無用單元收集堆中偎蘸,但計劃在將來的版本中實現(xiàn)步责。每個類文件包含了一個Java類或一個Java界面的編譯后的代碼≠骺啵可以說類文件是Java語言的執(zhí)行代碼文件蔓肯。為了保證類文件的平臺無關性,Java虛擬機規(guī)范中對類文件的格式也作了詳細的說明振乏。其具體細節(jié)請參考Sun公司的Java虛擬機規(guī)范蔗包。

1.2 Dalvik虛擬機-Android應用進程啟動過程

參考文章《深入理解Dalvik虛擬機- Android應用進程啟動過程分析》

? ??????Android的應用進程啟動是apk在manifest里申明的Activity、Service慧邮、BroadcastReceiver等組件被調(diào)起時而觸發(fā)的调限。我們以Activity為例,當點擊桌面上的應用圖標時误澳,桌面會調(diào)用startActivity耻矮,啟動manifest里申明的相應Launcher的Activity,而Activity的startActivity會通過Binder調(diào)用來到ActivityManagerService(AMS)里忆谓。AMS是system_server的一個服務裆装,負責管理Android系統(tǒng)的所有Activity的棧,邏輯比較復雜,在這里就不詳細分析哨免,以后專門寫AMS的專題茎活。AMS里startActivity的時候,如果發(fā)現(xiàn)這個應用進程沒有啟動琢唾,那么就會通過Zygote創(chuàng)建出這個進程载荔。

frameworks/base/services/Java/com/android/server/am/ActivityManagerService.java:

private?final?void?startProcessLocked(ProcessRecord?app, String?hostingType, String?hostingNameStr)?{??

? ? ...??

? ? ...??

???Process.ProcessStartResult?startResult?=?Process.start("android.app.ActivityThread", app.processName,?uid,?uid,?gids,?debugFlags,?mountExternal, app.info.targetSdkVersion, app.info.seinfo,?null);??

???...??

???...??

}??

????????顯然是通過調(diào)用Process.start來啟動進程,”android.app.ActivityThread” 這個參數(shù)是整個進程啟動的入口類采桃,后續(xù)的分析可以看到懒熙,進程被fork出來之后,就會調(diào)用android.app.ActivityThread的main函數(shù)普办。

frameworks/base/core/java/android/os/Process.java:

public?static?final?ProcessStartResult?start(final?String?processClass,?final?String?niceName,?int?uid,?int?gid,?int[]?gids,?int?debugFlags,?int?mountExternal,?int?targetSdkVersion, String?seInfo, String[]?zygoteArgs)?{??

????try?{??

????????return?startViaZygote(processClass,?niceName,?uid,?gid,?gids,? debugFlags, mountExternal, targetSdkVersion,?seInfo,?zygoteArgs);??

????}?catch?(ZygoteStartFailedEx?ex)?{??

????????Log.e(LOG_TAG, "Starting?VM?process?through?Zygote?failed");??

????????throw?new?RuntimeException("Starting?VM?process?through?Zygote?failed",?ex);??

????}??

}??


private?static?ProcessStartResult?startViaZygote(final?String?processClass,?final?String?niceName,?final?int?uid,?final?int?gid,?final?int[]?gids,?int?debugFlags,?int?mountExternal,?int?targetSdkVersion, String?seInfo, String[]?extraArgs)?throws?ZygoteStartFailedEx?{??

????synchronized(Process.class)?{??

????????ArrayList?argsForZygote?=?new?ArrayList();??


????????//?--runtime-init,?--setuid=,?--setgid=,??

????????//?and?--setgroups=?must?go?first??

????????argsForZygote.add("--runtime-init");??

????????argsForZygote.add("--setuid="?+?uid);??

????????argsForZygote.add("--setgid="?+?gid);??

????????if?((debugFlags?&?Zygote.DEBUG_ENABLE_JNI_LOGGING)?!=?0)?{??

????????????argsForZygote.add("--enable-jni-logging");??

????????}??

????????if?((debugFlags?&?Zygote.DEBUG_ENABLE_SAFEMODE)?!=?0)?{??

????????????argsForZygote.add("--enable-safemode");??

????????}??

????????if?((debugFlags?&?Zygote.DEBUG_ENABLE_DEBUGGER)?!=?0)?{??

????????????argsForZygote.add("--enable-debugger");??

????????}??

????????if?((debugFlags?&?Zygote.DEBUG_ENABLE_CHECKJNI)?!=?0)?{??

????????????argsForZygote.add("--enable-checkjni");??

????????}??

????????if?((debugFlags?&?Zygote.DEBUG_ENABLE_ASSERT)?!=?0)?{??

????????????argsForZygote.add("--enable-assert");??

????????}??

????????if?(mountExternal?==?Zygote.MOUNT_EXTERNAL_MULTIUSER)?{??

????????????argsForZygote.add("--mount-external-multiuser");??

????????}?else?if?(mountExternal?==?Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL)?{??

????????????argsForZygote.add("--mount-external-multiuser-all");??

????????}??

????????argsForZygote.add("--target-sdk-version="?+?targetSdkVersion);??


????????//TODO?optionally?enable?debuger??

????????//argsForZygote.add("--enable-debugger");??


????????//?--setgroups?is?a?comma-separated?list??

????????if?(gids?!=?null?&&?gids.length?>?0)?{??

????????????StringBuilder?sb?=?new?StringBuilder();??

????????????sb.append("--setgroups=");??


????????????int?sz?=?gids.length;??

????????????for?(int?i?=?0;?i?<?sz;?i++)?{??

????????????????if?(i?!=?0)?{??

????????????????????sb.append(',');??

????????????????}??

????????????????sb.append(gids[i]);??

????????????}??


????????????argsForZygote.add(sb.toString());??

????????}??


????????if?(niceName?!=?null)?{??

????????????argsForZygote.add("--nice-name="?+?niceName);??

????????}??


????????if?(seInfo?!=?null)?{??

????????????argsForZygote.add("--seinfo="?+?seInfo);??

????????}??


????????argsForZygote.add(processClass);??


????????if?(extraArgs?!=?null)?{??

????????????for?(String?arg?:?extraArgs)?{??

????????????????argsForZygote.add(arg);??

????????????}??

????????}??

????????return?zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),?argsForZygote);??

????}??

}??


private?static?ZygoteState?openZygoteSocketIfNeeded(String?abi)?throws?ZygoteStartFailedEx?{??

????if?(primaryZygoteState?==?null?||?primaryZygoteState.isClosed())?{??

????????try?{??

????????????primaryZygoteState?=?ZygoteState.connect(ZYGOTE_SOCKET);??

????????}?catch?(IOException?ioe)?{??

????????????throw?new?ZygoteStartFailedEx("Error?connecting?to?primary?zygote",?ioe);??

????????}??

????}??


????if?(primaryZygoteState.matches(abi))?{??

????????return?primaryZygoteState;??

????}??


????//?The?primary?zygote?didn't?match.?Try?the?secondary.??

????if?(secondaryZygoteState?==?null?||?secondaryZygoteState.isClosed())?{??

????????try?{??

????????????secondaryZygoteState?=?ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);??

????????}?catch?(IOException?ioe)?{??

????????????throw?new?ZygoteStartFailedEx("Error?connecting?to?secondary?zygote",?ioe);??

????????}??

????}??


????if?(secondaryZygoteState.matches(abi))?{??

????????return?secondaryZygoteState;??

????}??

????throw?new?ZygoteStartFailedEx("Unsupported?zygote?ABI:?"?+?abi);??

}

????????調(diào)用startViaZygote函數(shù)工扎,從代碼可以看到,Zygote創(chuàng)建進程是socket跨進程的調(diào)用泌豆。通過LocalSocket通信,來完成進程的創(chuàng)建吏饿,所以這里的Process.start只是一個Client端的調(diào)用踪危,實際是由Server端的接收到消息后處理的。

????????Server端是app_process這個進程里猪落,這是個常駐的系統(tǒng)服務贞远。

frameworks/base/cmds/app_process/app_main.cpp

class AppRuntime : public AndroidRuntime?

{?

}?

????????AppRuntime繼承自AndroidRuntime,AndroidRuntime類是libandroid_runtime.so里導出的笨忌,frameworks/base/core/jni/AndroidRuntime.cpp蓝仲。

????????app_process 這個進程在init.rc里會創(chuàng)建,

#init.rc??

service?zygote?/system/bin/app_process?-Xzygote?/system/bin?--zygote?--start-system-server??

????????app_process以zygote作為進程名官疲,這個adb shell進入手機ps一下就可以看到zygote袱结。

????????這個進程的main函數(shù)里有如下初始化代碼片段:

AppRuntime runtime;?

const char* argv0 = argv[0];?

// Process command line arguments?

// ignore argv[0]?

argc--;?

argv++;?


// Everything up to '--' or first non '-' arg goes to the vm?

int i = runtime.addVmArguments(argc, argv);?


// Parse runtime arguments.?Stop at first unrecognized option.?

bool zygote = false;?

bool startSystemServer = false;?

bool application = false;?

const char* parentDir = NULL;?

const char* niceName = NULL;?

const char* className = NULL;?

while (i < argc) {?

??? const char* arg =argv[i++];?

??? if (!parentDir) {?

??????? parentDir = arg;?

??? } else if (strcmp(arg,"--zygote") == 0) {?

??????? zygote = true;?

??????? niceName = "zygote";?

??? } else if (strcmp(arg,"--start-system-server") == 0) {?

??????? startSystemServer= true;?

??? } else if (strcmp(arg,"--application") == 0) {?

??????? application = true;?

??? } else if(strncmp(arg, "--nice-name=", 12) == 0) {?

??????? niceName = arg +12;?

??? } else {?

??????? className = arg;?

??????? break;?

??? }?

}?


if (niceName && *niceName) {?

??? setArgv0(argv0, niceName);?

???set_process_name(niceName);?

}?


runtime.mParentDir = parentDir;?


if (zygote) {?

???runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" :"");?

} else if (className) {?

??? // Remainder of argsget passed to startup class main()?

??? runtime.mClassName =className;?

??? runtime.mArgC = argc -i;?

??? runtime.mArgV = argv +i;?

???runtime.start("com.android.internal.os.RuntimeInit", application ?"application" : "tool");?

}

?????????第一個if是正常的Zygote進程啟動,執(zhí)行AndroidRuntime::start途凫,會調(diào)用ZygoteInit的類main函數(shù)垢夹。app_process的初始化就在ZygoteInit里,監(jiān)聽本地的socket维费,接收ZygoteClient的請求果元,當有請求來的時候,調(diào)用ZygoteConnection::runOnce犀盟,從而調(diào)用Zygote.forkAndSpecialize來創(chuàng)建新的進程而晒,并且調(diào)用RuntimeInit.zygoteInit做進程的初始化,初始化過程就會調(diào)用ActivityThread.main

void?AndroidRuntime::start(const?char*?className,?const?Vector&?options,?bool?zygote)??

...??

...??

/*?

?????*?Start?VM.??This?thread?becomes?the?main?thread?of?the?VM,?and?will?

?????*?not?return?until?the?VM?exits.?

?????*/??

????char*?slashClassName?=?toSlashClassName(className);??

????jclass?startClass?=?env->FindClass(slashClassName);??

????if?(startClass?==?NULL)?{??

????????ALOGE("JavaVM?unable?to?locate?class?'%s'\n",?slashClassName);??

????????/*?keep?going?*/??

????}?else?{??

????????jmethodID?startMeth?=?env->GetStaticMethodID(startClass,?"main", "([Ljava/lang/String;)V");??

????????if?(startMeth?==?NULL)?{??

????????????ALOGE("JavaVM?unable?to?find?main()?in?'%s'\n",?className);??

????????????/*?keep?going?*/??

????????}?else?{??

????????????env->CallStaticVoidMethod(startClass,?startMeth,?strArray);??

#if?0??

????????????if?(env->ExceptionCheck())??

????????????????threadExitUncaughtException(env);??

#endif??

????????}??

????}??

...??

}??

????????第二個當指定了className的時候阅畴,則用com.android.internal.os.RuntimeInit作為入口罚渐,指定className的場景是用am命令創(chuàng)建的進程的時候:

exec?app_process?$base/bin?com.android.commands.am.Am?"$@"???

?????????而我們正常的Zygote進程則走的是ZygoteInit。AndroidRuntime.start函數(shù)會調(diào)用startVm绽诚,frameworks/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startVm(JavaVM **pJavaVM, JNIEnv **pEnv)?

{?

??? ...?

??? init Args.version = JNI_VERSION_1_4;?

??? init Args.options = mOptions.editArray();?

??? init Args.nOptions = mOptions.size();?

???init Args.ignoreUnrecognized = JNI_FALSE;?

??? /*

???? * Initialize the VM.

???? *

???? * The JavaVM* isessentially per-process, and the JNIEnv* is per-thread.

???? * If this callsucceeds, the VM is ready, and we can start issuing

???? * JNI calls.

???? */?

??? if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {?

???????ALOGE("JNI_CreateJavaVM failed\n");?

??????? goto bail;?

??? }?

}?

????????startVm會初始化JavaVm和JNIEnv,最終是通過JNI_CreateJavaVM實現(xiàn)的豹障,JNI_CreateJavaVM是個接口,不同的虛擬機有不同的實現(xiàn)焦匈。

????????Dalvik虛擬機相關的代碼主要在下面幾個目錄下:

??????libcore/libdvm

??????libcore/dalvik

??????dalvik/vm/

????????Dalvik的JNI_CreateJavaVM在dalvik/vm/Jni.cpp 里實現(xiàn):

jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void*vm_args) {?

??? const JavaVMInitArgs *args = (JavaVMInitArgs*) vm_args;?

??? if(dvmIsBadJniVersion(args->version)) {?

??????? ALOGE("BadJNI version passed to CreateJavaVM: %d", args->version);?

??????? return JNI_EVERSION;?

??? }?


??? // TODO: don't allowcreation of multiple VMs -- one per customer for now?


??? /* zero globals; notstrictly necessary the first time a VM is started */?

??? memset(&gDvm, 0,sizeof(gDvm));?


??? /*

???? * Set up structuresfor JNIEnv and VM.

???? */?

??? JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));?

??? pVM->funcTable = &gInvokeInterface;?

??? pVM->envList = NULL;?

???dvmInitMutex(&pVM->envListLock);?


??? UniquePtr argv(new const char*[args->nOptions]);?

??? memset(argv.get(), 0,sizeof(char*) * (args->nOptions));?


??? /*

???? * Convert JNI args toargv.

???? *

???? * We have to pull outvfprintf/exit/abort, because they use the

???? *"extraInfo" field to pass function pointer "hooks" in.? We also

???? * look for the-Xcheck:jni stuff here.

???? */?

??? int argc = 0;?

??? for (int i = 0; i nOptions; i++) {?

??????? const char* optStr= args->options[i].optionString;?

??????? if (optStr ==NULL) {?

???????????dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d wasNULL\n", i);?

??????????? return JNI_ERR;?

??????? } else if(strcmp(optStr, "vfprintf") == 0) {?

???????????gDvm.vfprintfHook = (int (*)(FILE *, const char*,va_list))args->options[i].extraInfo;?

??????? } else if(strcmp(optStr, "exit") == 0) {?

??????????? gDvm.exitHook= (void (*)(int)) args->options[i].extraInfo;?

??????? } else if(strcmp(optStr, "abort") == 0) {?

??????????? gDvm.abortHook= (void (*)(void))args->options[i].extraInfo;?

??????? } else if(strcmp(optStr, "sensitiveThread") == 0) {?

???????????gDvm.isSensitiveThreadHook = (bool(*)(void))args->options[i].extraInfo;?

??????? } else if(strcmp(optStr, "-Xcheck:jni") == 0) {?

???????????gDvmJni.useCheckJni = true;?

??????? } else if(strncmp(optStr, "-Xjniopts:", 10) == 0) {?

??????????? char* jniOpts= strdup(optStr + 10);?

??????????? size_t jniOptCount = 1;?

??????????? for (char* p =jniOpts; *p != 0; ++p) {?

??????????????? if (*p ==',') {?

???????????????????++jniOptCount;?

??????????????????? *p =0;?

??????????????? }?

??????????? }?

????????? ??char* jniOpt = jniOpts;?

??????????? for (size_t i= 0; i < jniOptCount; ++i) {?

??????????????? if(strcmp(jniOpt, "warnonly") == 0) {?

???????????????????gDvmJni.warnOnly = true;?

??????????????? } else if(strcmp(jniOpt, "forcecopy") == 0) {?

???????????????????gDvmJni.forceCopy = true;?

??????????????? } else if(strcmp(jniOpt, "logThirdPartyJni") == 0) {?

???????????????????gDvmJni.logThirdPartyJni = true;?

??????????????? } else{?

???????????????????dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjnioptsoption '%s'\n",?

???????????????????????????jniOpt);?

???????????????????free(pVM);?

???????????????????free(jniOpts);?

??????????????????? return JNI_ERR;?

??????????????? }?

??????????????? jniOpt += strlen(jniOpt) + 1;?

??????????? }?

???????????free(jniOpts);?

??????? } else {?

??????????? /* regularoption */?

??????????? argv[argc++] = optStr;?

??????? }?

??? }?


??? if(gDvmJni.useCheckJni) {?

???????dvmUseCheckedJniVm(pVM);?

??? }?


??? if (gDvmJni.jniVm !=NULL) {?

??????? dvmFprintf(stderr,"ERROR: Dalvik only supports one VM per process\n");?

??????? free(pVM);?

??????? return JNI_ERR;?

??? }?

??? gDvmJni.jniVm = (JavaVM*) pVM;?


??? /*

???? * Create a JNIEnv forthe main thread.? We need to havesomething set up

???? * here because someof the class initialization we do when starting

???? * up the VM will callinto native code.

???? */?

??? JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);?


??? /* Initialize VM.*/?

??? gDvm.initializing = true;?

??? std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);?

??? gDvm.initializing =false;?


??? if (!status.empty()){?

??? ????free(pEnv);?

??????? free(pVM);?

???????ALOGW("CreateJavaVM failed: %s", status.c_str());?

??????? return JNI_ERR;?

??? }?


??? /*

???? * Success!? Return stuff to caller.

???? */?

??? dvmChangeStatus(NULL,THREAD_NATIVE);?

??? *p_env = (JNIEnv*)pEnv;?

??? *p_vm = (JavaVM*)pVM;?

???ALOGV("CreateJavaVM succeeded");?

??? return JNI_OK;?

}?

????????malloc完了之后血公,會調(diào)用dvmStartup完成虛擬機的啟動,dvmStartup定義在dalvik/vm/Init.cpp里:

/*

?* VM initialization.? Pass in any options provided on the commandline.

?* Do not pass in theclass name or the options for the class.

?*

?* Returns 0 on success.

?*/?

std::string dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, JNIEnv* pEnv)?

{?

??? ScopedShutdown scopedShutdown;?

???assert(gDvm.initializing);?


??? ALOGV("VM initargs (%d):", argc);?

??? for (int i = 0; i

??????? ALOGV("? %d: '%s'", i, argv[i]);?

??? }?

???setCommandLineDefaults();?


??? /*

???? * Process the optionflags (if any).

???? */?

??? int cc = processOptions(argc, argv, ignoreUnrecognized);?

??? if (cc != 0) {?

??????? if (cc < 0){?

???????????dvmFprintf(stderr, "\n");?

???????????usage("dalvikvm");?

??????? }?

??????? return "syntax error";?

??? }?


#if WITH_EXTRA_GC_CHECKS > 1?

??? /* only"portable" interp has the extra goodies */?

??? if (gDvm.executionMode!= kExecutionModeInterpPortable) {?

???????ALOGI("Switching to 'portable' interpreter for GCchecks");?

??????? gDvm.executionMode= kExecutionModeInterpPortable;?

??? }?

#endif?


??? /* Configure groupscheduling capabilities */?

??? if(!access("/dev/cpuctl/tasks", F_OK)) {?

??????? ALOGV("Usingkernel group scheduling");?

???????gDvm.kernelGroupScheduling = 1;?

??? } else {?

??????? ALOGV("Usingkernel scheduler policies");?

??? }?


??? /* configure signalhandling */?

??? if(!gDvm.reduceSignals)?

???????blockSignals();?


??? /* verify system pagesize */?

??? if(sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {?

??????? return StringPrintf("expected page size %d, got %d", SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));?

??? }?


??? /* mterp setup */?

??? ALOGV("UsingexecutionMode %d", gDvm.executionMode);?

???dvmCheckAsmConstants();?


??? /*

???? * Initializecomponents.

???? */?

???dvmQuasiAtomicsStartup();?

??? if(!dvmAllocTrackerStartup()) {?

??????? return "dvmAllocTrackerStartup failed";?

??? }?

??? if (!dvmGcStartup()){?

??????? return "dvmGcStartup failed";?

??? }?

??? if (!dvmThreadStartup()){?

??????? return "dvmThreadStartup failed";?

??? }?

??? if(!dvmInlineNativeStartup()) {?

??????? return "dvmInlineNativeStartup";?

??? }?

??? if(!dvmRegisterMapStartup()) {?

??????? return "dvmRegisterMapStartup failed";?

?? ?}?

??? if(!dvmInstanceofStartup()) {?

??????? return "dvmInstanceofStartup failed";?

??? }?

??? if(!dvmClassStartup()) {?

??????? return "dvmClassStartup failed";?

??? }?


??? /*

???? * At this point, thesystem is guaranteed to be sufficiently

???? * initialized that wecan look up classes and class members. This

???? * call populates thegDvm instance with all the class and member

???? * references that theVM wants to use directly.

???? */?

??? if(!dvmFindRequiredClassesAndMembers()) {?

? ? ? ? return "dvmFindRequiredClassesAndMembers failed";?

??? }?


??? if(!dvmStringInternStartup()) {?

??????? return "dvmStringInternStartup failed";?

??? }?

??? if (!dvmNativeStartup()){?

??????? return "dvmNativeStartup failed";?

??? }?

??? if(!dvmInternalNativeStartup()) {?

??????? return "dvmInternalNativeStartup failed";?

??? }?

??? if (!dvmJniStartup()){?

??????? return "dvmJniStartup failed";?

??? }?

??? if (!dvmProfilingStartup()){?

??????? return "dvmProfilingStartup failed";?

??? }?


??? /*

???? * Create a table ofmethods for which we will substitute an "inline"

???? * version forperformance.

???? */?

??? if(!dvmCreateInlineSubsTable()) {?

??????? return "dvmCreateInlineSubsTable failed";?

??? }?


??? /*

???? * Miscellaneous classlibrary validation.

???? */?

??? if(!dvmValidateBoxClasses()) {?

??????? return "dvmValidateBoxClasses failed";?

??? }?


??? /*

???? * Do the last bits ofThread struct initialization we need to allow

???? * JNI calls to work.

???? */?

??? if(!dvmPrepMainForJni(pEnv)) {?

??????? return "dvmPrepMainForJni failed";?

??? }?


??? /*

???? * Explicitlyinitialize java.lang.Class.? This doesn'thappen

???? * automaticallybecause it's allocated specially (it's an instance

???? * of itself).? Must happen before registration of systemnatives,

???? * which make somecalls that throw assertions if the classes they

???? * operate on aren'tinitialized.

???? */?

??? if(!dvmInitClass(gDvm.classJavaLangClass)) {?

??????? return "couldn't initialized java.lang.Class";?

??? }?


??? /*

???? * Register the systemnative methods, which are registered through JNI.

???? */?

??? if(!registerSystemNatives(pEnv)) {?

??????? return "couldn't register system natives";?

??? }?


??? /*

???? * Do some"late" initialization for the memory allocator.? This may

???? * allocate storageand initialize classes.

???? */?

??? if (!dvmCreateStockExceptions()){?

??????? return "dvmCreateStockExceptions failed";?

??? }?


??? /*

???? * At this point, theVM is in a pretty good state.? Finishprep on

???? * the main thread(specifically, create a java.lang.Thread object to go

? ???* along with our Thread struct).? Note we will probably be executing

???? * some interpretedclass initializer code in here.

???? */?

??? if(!dvmPrepMainThread()) {?

??????? return "dvmPrepMainThread failed";?

??? }?


??? /*

???? * Make sure wehaven't accumulated any tracked references.?The main

???? * thread should bestarting with a clean slate.

???? */?

??? if(dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) !=0)?

??? {?

???????ALOGW("Warning: tracked references remainpost-initialization");?

???????dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable,"MAIN");?

??? }?


??? /* general debuggingsetup */?

??? if(!dvmDebuggerStartup()) {?

??????? return "dvmDebuggerStartup failed";?

??? }?


??? if(!dvmGcStartupClasses()) {?

??????? return "dvmGcStartupClasses failed";?

??? }?


??? /*

???? * Init for eitherzygote mode or non-zygote mode.? The keydifference

???? * is that we don'tstart any additional threads in Zygote mode.

???? */?

??? if (gDvm.zygote){?

??????? if (!initZygote()){?

??????????? return "initZygote failed";?

??????? }?

???????dvmPostInitZygote();?

??? } else {?

??????? if(!dvmInitAfterZygote()) {?

??????????? return "dvmInitAfterZygote failed";?

??????? }?

??? }?


#ifndef NDEBUG?

??? if(!dvmTestHash())?

???????ALOGE("dvmTestHash FAILED");?

??? if (false /*noisy!*/&& !dvmTestIndirectRefTable())?

???????ALOGE("dvmTestIndirectRefTable FAILED");?

#endif?


??? if(dvmCheckException(dvmThreadSelf())) {?

???????dvmLogExceptionStackTrace();?

??????? return "Exception pending at end of VM initialization";?

??? }?


???scopedShutdown.disarm();?

??? return "";?

}?


????????dvmStartup調(diào)用了很多初始化接口缓熟,比如dvmClassStartup:

/*

?* Initialize thebootstrap class loader.

?*

?* Call this after thebootclasspath string has been finalized.

?*/?

bool dvmClassStartup()?

{?

??? /* make this arequirement -- don't currently support dirs in path */?

??? if(strcmp(gDvm.bootClassPathStr, ".") == 0) {?

??????? ALOGE("ERROR:must specify non-'.' bootclasspath");?

??????? return false;?

??? }?


??? gDvm.loadedClasses = dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);?


??? gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);?

??? if(gDvm.pBootLoaderAlloc == NULL)?

??????? return false;?


??? if (false) {?

???????linearAllocTests();?

??????? exit(0);?

??? }?


??? /*

???? * Class serialnumber.? We start with a high value tomake it distinct

???? * in binary dumps(e.g. hprof).

???? */?

??? gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;?


??? /*

???? * Set up the tablewe'll use for tracking initiating loaders for

???? * early classes.

???? * If it's NULL, wejust fall back to the InitiatingLoaderList in the

???? * ClassObject, soit's not fatal to fail this allocation.

???? */?

???gDvm.initiatingLoaderList = (InitiatingLoaderList*) calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));?


??? /*

???? * Create the initialclasses. These are the first objects constructed

???? * within the nascentVM.

???? */?

??? if(!createInitialClasses()) {?

??????? return false;?

??? }?


??? /*

???? * Process thebootstrap class path.? This means openingthe specified

???? * DEX or Jar filesand possibly running them through the optimizer.

???? */?

???assert(gDvm.bootClassPath == NULL);?

???processClassPath(gDvm.bootClassPathStr, true);?


??? if (gDvm.bootClassPath== NULL)?

??????? return false;?


??? return true;?

}?

????????這個類初始化gDvm的bootClassPath累魔,這樣就能執(zhí)行最早看到的ActivityThread的main函數(shù)。具體的調(diào)用棧為:ActivityThread.main-> ActivityThread.attach ->? ActivityThread.bindApplication ->Activity.handleBindApplication.

????????到目前的分析够滑,應用的ClassLoader還是BootClassPath垦写,只包含了Java和Android的類,Apk自身的類是找不到的彰触,會報ClassNotFound梯投,接下來就是介紹ClassLoader的加載過程。

????????handleBindApplication會初始化ApplicationInfo對象况毅,通getPackageInfo初始化LoadedApk分蓖,而LoadedApk則會創(chuàng)建這個apk對應的ClassLoader,這個ClassLoader是集成自BaseDexClassLoader尔许,加載了apk的dex么鹤。

ApplicationInfo?instrApp?=?new?ApplicationInfo();

instrApp.packageName?=?ii.packageName;

instrApp.sourceDir?=?ii.sourceDir;

instrApp.publicSourceDir?=?ii.publicSourceDir;

instrApp.dataDir?=?ii.dataDir;

instrApp.nativeLibraryDir?=?ii.nativeLibraryDir;

LoadedApk?pi?=?getPackageInfo(instrApp,?data.compatInfo,

appContext.getClassLoader(),?false,?true);

ContextImpl?instrContext?=?ContextImpl.createAppContext(this,?pi);

try{

????java.lang.ClassLoader?cl?=?instrContext.getClassLoader();

????mInstrumentation?=?(Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();

}catch(Exception?e)?{

? ??throw?new?RuntimeException("Unable?to?instantiate?instrumentation?+ data.instrumentationName +?":?" +?e.toString(),?e);

}

????????ContextImpl和Instrumentation的ClassLoader都已經(jīng)初始化為apk對應的BaseDexClassLoader,在這之后的類加載味廊,都會從這個ClassLoader對象里找蒸甜。ClassLoader是個樹狀結(jié)構(gòu),查找規(guī)則是先從父節(jié)點查找余佛,如果類已經(jīng)加載柠新,則直接返回加載好的Class。類的加載時機有兩個辉巡,一個是new操作符創(chuàng)建對象的登颓,一個是直接調(diào)用ClassLoader的loadClass的時候,new操作符的代碼在dalvik解釋器里红氯,我們下一個專題會講框咙,最后會調(diào)用dvmResolveClass(dalvik/vm/oo/Resolve.cpp)來加載類。loadClass的實現(xiàn)如下:

libcore/libdvm/src/main/java/java/lang/ClassLoader.java

protected Class<?>?loadClass(String className, booleanresolve) throws ClassNotFoundException {?

??????? Class<?>?clazz = findLoadedClass(className);?


??????? if (clazz == null){?

??????????? try {?

??????????????? clazz = parent.loadClass(className, false);?

??????????? } catch(ClassNotFoundException e) {?

??????????????? // Don'twant to see this.?

??????????? }?


??????????? if (clazz ==null) {?

??????????????? clazz = findClass(className);?

??????????? }?

??????? }?


??????? return clazz;?

}

????????它首先看自己的parent是否已經(jīng)加載過class了痢甘,加載了就返回喇嘱,沒有就調(diào)用BaseDexClassLoader的findClass。

@Override

protected?Class?findClass(String?name)?throws?ClassNotFoundException?{

????List?suppressedExceptions?=?new?ArrayList();

????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;

}

????????BaseDexClassLoader的findClass塞栅,實際是調(diào)用了DexPathList的findClass者铜。上面的整個過程就是Android的apk從啟動,一直到ClassLoader被初始化完,之后就是走Android的Activity正常生命周期了作烟。

????????下篇介紹一下Dalvik虛擬機的解析器的工作原理愉粤。

1.3 ART虛擬機運行機制

????????前面兩篇文章介紹了ART的啟動過程,而在啟動之后拿撩,我們感興趣的就是ART是怎么運行的衣厘。回顧一下虛擬機系列的前面幾篇文章压恒,我們可以理一下思路:

一影暴,apk以進程的形式運行,進程的創(chuàng)建是由zygote探赫。

參考文章《深入理解Dalvik虛擬機- Android應用進程啟動過程分析》

二型宙,進程運行起來之后,初始化JavaVM

參考文章《深入理解ART虛擬機—虛擬機的啟動》

三伦吠,JavaVM創(chuàng)建之后妆兑,我們就有了JNINativeInterface,里面包含了所有的Java接口毛仪,比如FindClass搁嗓,NewObject,CallObjectMethod等

參考文章《深入理解ART虛擬機—虛擬機的啟動》

四潭千,Java的運行時的功能簡單來說分為:類的加載和函數(shù)Method的執(zhí)行

參考文章《深入理解Dalvik虛擬機- 解釋器的運行機制》

art的JNINativeInterface的定義如下:

const?JNINativeInterface?gJniNativeInterface?=?{??

??nullptr,??//?reserved0.??

??nullptr,??//?reserved1.??

??nullptr,??//?reserved2.??

??nullptr,??//?reserved3.??

??JNI::GetVersion,??

??JNI::DefineClass,??

??JNI::FindClass,??

??JNI::FromReflectedMethod,??

??JNI::FromReflectedField,??

??JNI::ToReflectedMethod,??

??JNI::GetSuperclass,??

??JNI::IsAssignableFrom,??

??JNI::ToReflectedField,??

??JNI::Throw,??

??JNI::ThrowNew,??

??JNI::ExceptionOccurred,??

??JNI::ExceptionDescribe,??

??JNI::ExceptionClear,??

??JNI::FatalError,??

??JNI::PushLocalFrame,??

??JNI::PopLocalFrame,??

??JNI::NewGlobalRef,??

??JNI::DeleteGlobalRef,??

??JNI::DeleteLocalRef,??

??JNI::IsSameObject,??

??JNI::NewLocalRef,??

??JNI::EnsureLocalCapacity,??

??JNI::AllocObject,??

??JNI::NewObject,??

??JNI::NewObjectV,??

??JNI::NewObjectA,??

??JNI::GetObjectClass,??

??JNI::IsInstanceOf,??

??JNI::GetMethodID,??

??JNI::CallObjectMethod,??

??JNI::CallObjectMethodV,??

??JNI::CallObjectMethodA,??

??JNI::CallBooleanMethod,??

??JNI::CallBooleanMethodV,??

??JNI::CallBooleanMethodA,??

??JNI::CallByteMethod,??

??JNI::CallByteMethodV,??

??JNI::CallByteMethodA,??

??JNI::CallCharMethod,??

??JNI::CallCharMethodV,??

??JNI::CallCharMethodA,??

??JNI::CallShortMethod,??

??JNI::CallShortMethodV,??

??JNI::CallShortMethodA,??

??JNI::CallIntMethod,??

??JNI::CallIntMethodV,??

??JNI::CallIntMethodA,??

??JNI::CallLongMethod,??

??JNI::CallLongMethodV,??

??JNI::CallLongMethodA,??

??JNI::CallFloatMethod,??

??JNI::CallFloatMethodV,??

??JNI::CallFloatMethodA,??

??JNI::CallDoubleMethod,??

??JNI::CallDoubleMethodV,??

??JNI::CallDoubleMethodA,??

??JNI::CallVoidMethod,??

??JNI::CallVoidMethodV,??

??JNI::CallVoidMethodA,??

??JNI::CallNonvirtualObjectMethod,??

??JNI::CallNonvirtualObjectMethodV,??

??JNI::CallNonvirtualObjectMethodA,??

??JNI::CallNonvirtualBooleanMethod,??

??JNI::CallNonvirtualBooleanMethodV,??

??JNI::CallNonvirtualBooleanMethodA,??

??JNI::CallNonvirtualByteMethod,??

??JNI::CallNonvirtualByteMethodV,??

??JNI::CallNonvirtualByteMethodA,??

??JNI::CallNonvirtualCharMethod,??

??JNI::CallNonvirtualCharMethodV,??

??JNI::CallNonvirtualCharMethodA,??

??JNI::CallNonvirtualShortMethod,??

??JNI::CallNonvirtualShortMethodV,??

??JNI::CallNonvirtualShortMethodA,??

??JNI::CallNonvirtualIntMethod,??

??JNI::CallNonvirtualIntMethodV,??

??JNI::CallNonvirtualIntMethodA,??

??JNI::CallNonvirtualLongMethod,??

??JNI::CallNonvirtualLongMethodV,??

??JNI::CallNonvirtualLongMethodA,??

??JNI::CallNonvirtualFloatMethod,??

??JNI::CallNonvirtualFloatMethodV,??

??JNI::CallNonvirtualFloatMethodA,??

??JNI::CallNonvirtualDoubleMethod,??

??JNI::CallNonvirtualDoubleMethodV,??

??JNI::CallNonvirtualDoubleMethodA,??

??JNI::CallNonvirtualVoidMethod,??

??JNI::CallNonvirtualVoidMethodV,??

??JNI::CallNonvirtualVoidMethodA,??

??JNI::GetFieldID,??

??JNI::GetObjectField,??

??JNI::GetBooleanField,??

??JNI::GetByteField,??

??JNI::GetCharField,??

??JNI::GetShortField,??

??JNI::GetIntField,??

??JNI::GetLongField,??

??JNI::GetFloatField,??

??JNI::GetDoubleField,??

??JNI::SetObjectField,??

??JNI::SetBooleanField,??

??JNI::SetByteField,??

??JNI::SetCharField,??

??JNI::SetShortField,??

??JNI::SetIntField,??

??JNI::SetLongField,??

??JNI::SetFloatField,??

??JNI::SetDoubleField,??

??JNI::GetStaticMethodID,??

??JNI::CallStaticObjectMethod,??

??JNI::CallStaticObjectMethodV,??

??JNI::CallStaticObjectMethodA,??

??JNI::CallStaticBooleanMethod,??

??JNI::CallStaticBooleanMethodV,??

??JNI::CallStaticBooleanMethodA,??

??JNI::CallStaticByteMethod,??

??JNI::CallStaticByteMethodV,??

??JNI::CallStaticByteMethodA,??

??JNI::CallStaticCharMethod,??

??JNI::CallStaticCharMethodV,??

??JNI::CallStaticCharMethodA,??

??JNI::CallStaticShortMethod,??

??JNI::CallStaticShortMethodV,??

??JNI::CallStaticShortMethodA,??

??JNI::CallStaticIntMethod,??

??JNI::CallStaticIntMethodV,??

??JNI::CallStaticIntMethodA,??

??JNI::CallStaticLongMethod,??

??JNI::CallStaticLongMethodV,??

??JNI::CallStaticLongMethodA,??

??JNI::CallStaticFloatMethod,??

??JNI::CallStaticFloatMethodV,??

??JNI::CallStaticFloatMethodA,??

??JNI::CallStaticDoubleMethod,??

??JNI::CallStaticDoubleMethodV,??

??JNI::CallStaticDoubleMethodA,??

??JNI::CallStaticVoidMethod,??

??JNI::CallStaticVoidMethodV,??

??JNI::CallStaticVoidMethodA,??

??JNI::GetStaticFieldID,??

??JNI::GetStaticObjectField,??

??JNI::GetStaticBooleanField,??

??JNI::GetStaticByteField,??

??JNI::GetStaticCharField,??

??JNI::GetStaticShortField,??

??JNI::GetStaticIntField,??

??JNI::GetStaticLongField,??

??JNI::GetStaticFloatField,??

??JNI::GetStaticDoubleField,??

??JNI::SetStaticObjectField,??

??JNI::SetStaticBooleanField,??

??JNI::SetStaticByteField,??

??JNI::SetStaticCharField,??

??JNI::SetStaticShortField,??

??JNI::SetStaticIntField,??

??JNI::SetStaticLongField,??

??JNI::SetStaticFloatField,??

??JNI::SetStaticDoubleField,??

??JNI::NewString,??

??JNI::GetStringLength,??

??JNI::GetStringChars,??

??JNI::ReleaseStringChars,??

??JNI::NewStringUTF,??

??JNI::GetStringUTFLength,??

??JNI::GetStringUTFChars,??

??JNI::ReleaseStringUTFChars,??

??JNI::GetArrayLength,??

??JNI::NewObjectArray,??

??JNI::GetObjectArrayElement,??

??JNI::SetObjectArrayElement,??

??JNI::NewBooleanArray,??

??JNI::NewByteArray,??

??JNI::NewCharArray,??

??JNI::NewShortArray,??

??JNI::NewIntArray,??

??JNI::NewLongArray,??

??JNI::NewFloatArray,??

??JNI::NewDoubleArray,??

??JNI::GetBooleanArrayElements,??

??JNI::GetByteArrayElements,??

??JNI::GetCharArrayElements,??

??JNI::GetShortArrayElements,??

??JNI::GetIntArrayElements,??

??JNI::GetLongArrayElements,??

??JNI::GetFloatArrayElements,??

??JNI::GetDoubleArrayElements,??

??JNI::ReleaseBooleanArrayElements,??

??JNI::ReleaseByteArrayElements,??

??JNI::ReleaseCharArrayElements,??

??JNI::ReleaseShortArrayElements,??

??JNI::ReleaseIntArrayElements,??

??JNI::ReleaseLongArrayElements,??

??JNI::ReleaseFloatArrayElements,??

??JNI::ReleaseDoubleArrayElements,??

??JNI::GetBooleanArrayRegion,??

??JNI::GetByteArrayRegion,??

??JNI::GetCharArrayRegion,??

??JNI::GetShortArrayRegion,??

??JNI::GetIntArrayRegion,??

??JNI::GetLongArrayRegion,??

??JNI::GetFloatArrayRegion,??

??JNI::GetDoubleArrayRegion,??

??JNI::SetBooleanArrayRegion,??

??JNI::SetByteArrayRegion,??

??JNI::SetCharArrayRegion,??

??JNI::SetShortArrayRegion,??

??JNI::SetIntArrayRegion,??

??JNI::SetLongArrayRegion,??

??JNI::SetFloatArrayRegion,??

??JNI::SetDoubleArrayRegion,??

??JNI::RegisterNatives,??

??JNI::UnregisterNatives,??

??JNI::MonitorEnter,??

??JNI::MonitorExit,??

??JNI::GetJavaVM,??

??JNI::GetStringRegion,??

??JNI::GetStringUTFRegion,??

??JNI::GetPrimitiveArrayCritical,??

??JNI::ReleasePrimitiveArrayCritical,??

??JNI::GetStringCritical,??

??JNI::ReleaseStringCritical,??

??JNI::NewWeakGlobalRef,??

??JNI::DeleteWeakGlobalRef,??

??JNI::ExceptionCheck,??

??JNI::NewDirectByteBuffer,??

??JNI::GetDirectBufferAddress,??

??JNI::GetDirectBufferCapacity,??

??JNI::GetObjectRefType,??

};??

????????這些函數(shù)的定義在jni_internal.cc谱姓。

????????我們要分析art的運行機制借尿,就需要弄清楚類的加載和art函數(shù)的執(zhí)行:

????一刨晴,類的加載

????????dalvik的類加載我們已經(jīng)在《深入理解Dalvik虛擬機-?Android應用進程啟動過程分析》分析了,Android應用進程啟動的時候會創(chuàng)建BaseDexClassLoader路翻,這個BaseDexClassLoader包含了自身apk狈癞。再回顧一下過程:

????1, app_process作為zygote server通過local socket處理進程創(chuàng)建請求,zygote server是在ZygoteInit.main函數(shù)里調(diào)用ZygoteInit.runSelectLoop監(jiān)聽茂契。

????2, 接收到zygote client的fork請求之后蝶桶,調(diào)用ZygoteConnection.runOnce,調(diào)用Zygote.forkAndSpecialize創(chuàng)建新進程掉冶。

????3, 進程創(chuàng)建之后真竖,由ZygoteConnection.handleParentProc來初始化進程,最終會調(diào)用ActivityThread.main函數(shù)厌小。

????4, ActivityThread.main -> ActivityThread.attach ->?ActivityThread.bindApplication -> Activity.handleBindApplication恢共,handleBindApplication會初始化BaseDexClassLoader。

????5, 類的加載經(jīng)過了ClassLoader.loadClass->BaseDexClassLoader.findClass->DexPathList.findClass->DexFile.loadClassBinaryName->DexFile.defineClassNative->DexFile_defineClassNative(runtime/native/dalvik_system_DexFile.cc)

????????這個初始化過程璧亚,art和dalvik都是一樣的讨韭。art的DexFile_defineClassNative由ClassLinker的DefineClass來加載類。

static jclass DexFile_defineClassNative(JNIEnv* env, jclass,jstring javaName, jobject javaLoader, jobject cookie){?

? ? std::unique_ptr> dex_files = ConvertJavaArrayToNative(env, cookie);?

? ? if (dex_files.get() ==nullptr) {?

??? ????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::stringdescriptor(DotToDescriptor(class_name.c_str()));?

? const size_thash(ComputeModifiedUtf8Hash(descriptor.c_str()));?

? for (auto& dex_file: *dex_files) {?

? ????constDexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);?

? ? ? if (dex_class_def !=nullptr) {?

????? ????ScopedObjectAccesssoa(env);?

????? ????ClassLinker*class_linker = Runtime::Current()->GetClassLinker();?

? ? ? ? ? class_linker->RegisterDexFile(*dex_file);?

? ? ? ? ? StackHandleScope<1> hs(soa.Self());?

? ? ? ? ? Handleclass_loader(hs.NewHandle(soa.Decode(javaLoader)));?

????? ????mirror::Class*result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,? class_loader, *dex_file, *dex_class_def);?

????? ????if (result !=nullptr) {?

? ? ? ? ? ? ? VLOG(class_linker)<< "DexFile_defineClassNative returning " << result << " for " << class_name.c_str();?

? ? ? ? ? ? ? return soa.AddLocalReference(result);?

????? ????}?

? ? ? }?

? }?

? VLOG(class_linker)<< "Failed to find dex_class_def " <

? return nullptr;?

}

????????類的加載除了創(chuàng)建Class只外,還有加載類的字段和方法透硝,這個由ClassLinker::LoadClass來完成狰闪。

void ClassLinker::LoadClass(Thread* self, const DexFile&dex_file, const DexFile::ClassDef& dex_class_def, Handle klass) {?

????const uint8_t*class_data = dex_file.GetClassData(dex_class_def);?

? ? if (class_data ==nullptr) {?

??? ????return;? // no fields or methods - for example a markerinterface?

? ? }?

? ? bool has_oat_class = false;?

? ? if(Runtime::Current()->IsStarted() &&!Runtime::Current()->IsAotCompiler()) {?

??? ????OatFile::OatClassoat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class);?

? ? ? ? if (has_oat_class){?

? ? ? ? ? ?LoadClassMembers(self, dex_file, class_data, klass,&oat_class);?

??? ????}?

? ? }?

? ? if (!has_oat_class){?

? ? ? ? LoadClassMembers(self, dex_file, class_data, klass, nullptr);?

? ? }?

}

????二,函數(shù)的執(zhí)行

????????一旦類的加載完成濒生,那么就可以調(diào)用類的成員函數(shù)了埋泵,之前的解釋器運行機制那篇文章介紹過,Java的執(zhí)行是以Method為執(zhí)行單元的甜攀,所以我們分析art的運行機制秋泄,其實就是分析Method的運行機制。

????????《深入理解Dalvik虛擬機- Android應用進程啟動過程分析》可知规阀,ActivityThread是進程在啟動的時候傳類名恒序,在進程啟動之后,由handleParentProc執(zhí)行main函數(shù)谁撼,因此第一個被執(zhí)行的java函數(shù)是ActivityThread.main歧胁。

Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, null);?

????????ActivityThread.main是最終由AndroidRuntime::callMain執(zhí)行

status_tAndroidRuntime::callMain(const String8& className, jclass clazz, const Vector& args)?

{?

??? JNIEnv* env;?

??? jmethodID methodId;?


??? ALOGD("Calling main entry %s",className.string());?


??? env = getJNIEnv();?

??? if (clazz == NULL || env == NULL) {?

??????? return UNKNOWN_ERROR;?

??? }?


??? methodId = env->GetStaticMethodID(clazz,"main", "([Ljava/lang/String;)V");?

??? if (methodId == NULL) {?

??????? ALOGE("ERROR: could not findmethod %s.main(String[])\n", className.string());?

??????? return UNKNOWN_ERROR;?

??? }?


??? /*

???? * We want to call main() with a Stringarray with our arguments in it.

???? * Create an array and populate it.

???? */?

??? jclass stringClass;?

??? jobjectArray strArray;?

??? const size_t numArgs = args.size();?

??? stringClass = env->FindClass("java/lang/String");?

??? strArray = env->NewObjectArray(numArgs,stringClass, NULL);?


??? for (size_t i = 0; i < numArgs; i++){?

??????? jstring argStr = env->NewStringUTF(args[i].string());?

??????? env->SetObjectArrayElement(strArray,i, argStr);?

??? }?


??? env->CallStaticVoidMethod(clazz,methodId, strArray);?

??? return NO_ERROR;?

}?

????????實際會調(diào)用JNINativeInterface的CallStaticVoidMethod,上面已經(jīng)介紹過厉碟,該函數(shù)的定義在runtime/jni_internal.cc里:

static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {?

? ? va_list ap;?

? ? va_start(ap,mid);?

? ? CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);?

? ? ScopedObjectAccesssoa(env);?

? ? InvokeWithVarArgs(soa, nullptr, mid, ap);?

? ? va_end(ap);?

}?

????????InvokeWithVarArgs是執(zhí)行函數(shù)的入口喊巍,定義在runtime/reflection.cc,最終是調(diào)用了ArtMethod::Invoke箍鼓。

JValue InvokeWithVarArgs(constScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {?

? ? // We want to make surethat the stack is not within a small distance from the?

? ? // protected region incase we are calling into a leaf function whose stack?

? ? // check has beenelided.?

? ? if(UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {?

???????ThrowStackOverflowError(soa.Self());?

??? ????return JValue();?

? ? }?


? ? ArtMethod* method = soa.DecodeMethod(mid);?

? ? bool is_string_init = method->GetDeclaringClass()->IsStringClass() &&method->IsConstructor();?

? ? if (is_string_init){?

??? ????// Replace calls toString. with equivalent StringFactory call.?

??? ????method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));?

? ? }?

? ? mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode(obj);?

? ? uint32_t shorty_len = 0;?

? ? const char* shorty = method->GetShorty(&shorty_len);?

? ? JValue result;?

? ? ArgArrayarg_array(shorty, shorty_len);?

? ? arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);?

? ? InvokeWithArgArray(soa, method, &arg_array, &result, shorty);?

? ? if (is_string_init){?

??? ????// For string init,remap original receiver to StringFactory result.?

???????UpdateReference(soa.Self(), obj, result.GetL());?

? ? }?

? ? return result;?

}?

static void InvokeWithArgArray(constScopedObjectAccessAlreadyRunnable& soa, ArtMethod* method, ArgArray* arg_array, JValue* result, const char* shorty)? SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {?

? ? uint32_t* args = arg_array->GetArray();?

? ? if (UNLIKELY(soa.Env()->check_jni)){?

???????CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(sizeof(void*)), args);?

? ? }?

? ? method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result,shorty);?

}?

????????我們知道ART的運行模式是AOT的崭参,在apk安裝的時候,每個DexMethod都會由dex2oat編譯成目標代碼款咖,而不再是虛擬機執(zhí)行的字節(jié)碼何暮,但同時Dex字節(jié)碼仍然還在OAT里存在,所以ART的代碼執(zhí)行既支持QuickCompiledCode模式铐殃,也同時支持解釋器模式以及JIT執(zhí)行模式海洼。看ArtMethod::Invoke

void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*result, const char* shorty){?

? ? if (UNLIKELY(__builtin_frame_address(0) GetStackEnd())) {?

? ? ? ? ThrowStackOverflowError(self);?

??? ????return;?

? ? }?


? ? if (kIsDebugBuild) {?

??? ????self->AssertThreadSuspensionIsAllowable();?

??? ????CHECK_EQ(kRunnable, self->GetState());?

???????CHECK_STREQ(GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(), shorty);?

? ? }?


? ? // Push a transition back into managed code onto the linked list in thread.?

? ? ManagedStack fragment;?

? ? self->PushManagedStackFragment(&fragment);?


? ? Runtime* runtime = Runtime::Current();?

? ? // Call the invoke stub, passing everything as arguments.?

? ? // If the runtime is not yet started or it is required by the debugger, then perform the?

? ? // Invocation by the interpreter.?

? ? if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) {?

??? ????if (IsStatic()) {?

? ? ? ? ? ? art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result);?

? ? ? ? } else {?

? ? ? ? ????mirror::Object* receiver = reinterpret_cast*>(&args[0])->AsMirrorPtr();?

? ? ? ? ? ? art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args + 1, result);?

??? ????}?

? ? } else {?

? ? ? ? DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(),sizeof(void*));?

????????constexpr bool kLogInvocationStartAndReturn = false;?

??????? bool have_quick_code = GetEntryPointFromQuickCompiledCode()!= nullptr;?

??? ????if (LIKELY(have_quick_code)) {?

? ? ? ? ? ? if (kLogInvocationStartAndReturn) {?

??????? ????????LOG(INFO) << StringPrintf( "Invoking '%s' quick code=%pstatic=%d", PrettyMethod(this).c_str(), GetEntryPointFromQuickCompiledCode(), static_cast(IsStatic() ? 1 : 0));?

? ? ? ? ? ? }?


? ? ? ? ? ? // Ensure that we won't be accidentally calling quick compiled code when -Xint.?

? ? ? ? ? ? if (kIsDebugBuild &&runtime->GetInstrumentation()->IsForcedInterpretOnly()) {?

??????? ????????DCHECK(!runtime->UseJit());?

??????? ????????CHECK(IsEntrypointInterpreter()) << "Don't call compiled code when -Xint " << PrettyMethod(this);?

? ? ? ? ? ? }?


#if defined(__LP64__) || defined(__arm__) || defined(__i386__)?

? ? ? ? ? ? if (!IsStatic()) {?

??????? ????????(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);?

? ? ? ? ? ? } else {?

??????? ????????(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);?

? ? ? ? ? ? }?

#else?

? ? ? ? ? ? (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);?

#endif?

? ? ? ? ? ? if (UNLIKELY(self->GetException() ==Thread::GetDeoptimizationException())) {?

??????? ????????// Unusual case where we were running generated code and an?

??????? ????????// exception was thrown to force the activations to be removed from the?

??????? ????????// stack. Continue execution in the interpreter.?

??????? ????????self->ClearException();?

??????? ????????ShadowFrame* shadow_frame = self->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);?

? ? ? ? ? ? ? ? result->SetJ(self->PopDeoptimizationReturnValue().GetJ());?

??????? ????????self->SetTopOfStack(nullptr);?

? ? ? ? ? ? ? ? self->SetTopOfShadowStack(shadow_frame);?

? ? ? ? ? ? ? ? interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);?

? ? ? ? ? ? }?

? ? ? ? ? ? if (kLogInvocationStartAndReturn) {?

??????? ????????LOG(INFO) < GetEntryPointFromQuickCompiledCode());?

? ? ? ? ? ? }?

? ? ? ? } else {?

? ? ? ? ? ? LOG(INFO) << "Not invoking '"<< PrettyMethod(this) << "' code=null";?

? ? ? ? ? ? if (result != nullptr) {?

??????? ????????result->SetJ(0);?

? ? ? ? ? ? }?

??? ????}?

? ? }?


? ? // Pop transition.?

? ? self->PopManagedStackFragment(fragment);?

}?


????????Invoke可以進入OAT富腊,Interpreter模式執(zhí)行Method坏逢,如果當前是Interpreter模式,就調(diào)用art::interpreter::EnterInterpreterFromInvoke赘被,如果是OAT模式是整,就調(diào)用art_quick_invoke_stub/art_quick_invoke_static_stub。

????????EnterInterpreterFromInvoke函數(shù)里會判斷是native還是解釋器執(zhí)行:

voidEnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object*receiver, uint32_t* args,JValue* result) {?

? ????DCHECK_EQ(self, Thread::Current());?

? ????bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();?

? ????if (UNLIKELY(__builtin_frame_address(0) GetStackEndForInterpreter(implicit_check))) {?

??? ????????ThrowStackOverflowError(self);?

??? ????????return;?

????? }?


? ????const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");?

? ????const DexFile::CodeItem* code_item = method->GetCodeItem();?

? ????uint16_t num_regs;?

? ????uint16_t num_ins;?

? ????if (code_item != nullptr) {?

? ? ? ? ? num_regs =?code_item->registers_size_;?

? ? ? ? ? num_ins = code_item->ins_size_;?

? ????} else if (method->IsAbstract()) {?

???????????self->EndAssertNoThreadSuspension(old_cause);?

??? ????????ThrowAbstractMethodError(method);?

??? ????????return;?

? ????} else {?

??? ????????DCHECK(method->IsNative());?

??? ????????num_regs = num_ins =ArtMethod::NumArgRegisters(method->GetShorty());?

??? ????????if (!method->IsStatic()) {?

? ? ? ? ? ? ? ? num_regs++;?

? ? ? ? ? ? ? ? num_ins++;?

??? ????????}?

? ? ? ? }?

? ? ? ? // Set up shadow frame with matching number of reference slots to vregs.?

? ? ? ? ShadowFrame* last_shadow_frame = self->GetManagedStack()->GetTopShadowFrame();?

? ? ? ? void* memory = alloca(ShadowFrame::ComputeSize(num_regs));?

? ? ? ? ShadowFrame*shadow_frame(ShadowFrame::Create(num_regs, last_shadow_frame, method, 0, memory));?

? ? ? ? self->PushShadowFrame(shadow_frame);?


? ? ? ? size_t cur_reg = num_regs - num_ins;?

? ? ? ? if (!method->IsStatic()) {?

? ? ? ? ? ? CHECK(receiver != nullptr);?

??? ????????shadow_frame->SetVRegReference(cur_reg, receiver);?

??? ????????++cur_reg;?

? ? ? ? }?

? ? ? ? uint32_t shorty_len = 0;?

? ? ? ? const char* shorty = method->GetShorty(&shorty_len);?

? ? ? ? for (size_t shorty_pos = 0, arg_pos = 0; cur_reg< num_regs; ++shorty_pos, ++arg_pos, cur_reg++) {?

??? ????????DCHECK_LT(shorty_pos + 1, shorty_len);?

??? ????????switch (shorty[shorty_pos + 1]) {?

? ? ? ? ? ? ? ? ?case 'L': {?

? ? ? ? ? ? ? ? ? ? ?Object* o = reinterpret_cast*>(&args[arg_pos])->AsMirrorPtr();?

? ? ? ? ? ? ? ? ? ? ?shadow_frame->SetVRegReference(cur_reg, o);?

? ? ? ? ? ? ? ? ? ? ?break;?

? ? ? ? ? ? ? ? ? }?

????? ????????case 'J':?

? ? ? ? ? ? ? case 'D': {?

? ? ? ? ? ? ? ? ? uint64_t wide_value = (static_cast(args[arg_pos + 1]) << 32) |args[arg_pos];?

? ? ? ? ? ? ? ? ? shadow_frame->SetVRegLong(cur_reg,wide_value);?

? ? ? ? ? ? ? ? ? cur_reg++;?

? ? ? ? ? ? ? ? ? arg_pos++;?

? ? ? ? ? ? ? ? ? break;?

????? ????????}?

????? ????????default:?

? ? ? ? ? ? ? ? ? shadow_frame->SetVReg(cur_reg,args[arg_pos]);?

? ? ? ? ? ? ? ? ? break;?

? ? ? ? ? ? ? }?

? ????????}?

? ????????self->EndAssertNoThreadSuspension(old_cause);?

? ????????// Do this after populating the shadow frame in case EnsureInitialized causes a GC.?

? ????????if (method->IsStatic() &&UNLIKELY(!method->GetDeclaringClass()->IsInitialized())) {?

? ? ? ? ? ? ? ClassLinker* class_linker = Runtime::Current()->GetClassLinker();?

? ? ? ? ? ? ? StackHandleScope<1> hs(self);?

? ? ? ? ? ? ? Handleh_class(hs.NewHandle(method->GetDeclaringClass()));?

? ? ? ? ? ? ? if(UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))){?

????? ????????????CHECK(self->IsExceptionPending());?

????? ????????????self->PopShadowFrame();?

????? ????????????return;?

? ? ? ? ? ? ? }?

? ????????}?

? ????????if (LIKELY(!method->IsNative())) {?

? ? ? ? ? ? ? JValue r = Execute(self, code_item,*shadow_frame, JValue());?

? ? ? ? ? ? ? if (result != nullptr) {?

????? ????????????*result = r;?

? ? ? ? ? ? ? }?

? ????????} else {?

? ? ? ? ? ? ? // We don't expect to be asked to interpret native code (which is entered via a JNI compiler?

? ? ? ? ? ? ? // generated stub) except during testing and image writing.?

? ? ? ? ? ? ? // Update args to be the args in the shadow frame since the input ones could hold stale?

? ? ? ? ? ? ? // references pointers due to movingGC.?

? ? ? ? ? ? ? args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);?

? ? ? ? ? ? ? if (!Runtime::Current()->IsStarted()){?

????? ????????????UnstartedRuntime::Jni(self, method, receiver, args, result);?

? ? ? ? ? ? ? } else {?

????? ????????????InterpreterJni(self, method, shorty, receiver, args, result);?

? ? ? ? ? ? ? }?

? ????????}?

? ????????self->PopShadowFrame();?

? ? ? }?

????????這個函數(shù)前面部分都在做參數(shù)壓棧操作民假,最后幾行進入主題浮入,如果不是Native,那么調(diào)用Execute執(zhí)行阳欲;Native函數(shù)則調(diào)用InterpreterJni舵盈。Execute就是art的解釋器代碼陋率,Dex的字節(jié)碼是通過ArtMethod::GetCodeItem函數(shù)獲得,由Execute逐條執(zhí)行秽晚。InterpreterJni通過GetEntryPointFromJni來獲得native的函數(shù)瓦糟,并執(zhí)行。

if (LIKELY(!method->IsNative())) {?

??? JValue r = Execute(self, code_item, *shadow_frame, JValue());?

??? if (result != nullptr){?

? ? ? ? *result = r;?

??? }?

} else {?

??? // We don't expect to be asked to interpret native code (which is entered via a JNI compiler?

??? // generated stub)except during testing and image writing.?

??? // Update args to bethe args in the shadow frame since the input ones could hold stale?

??? // references pointersdue to moving GC.?

??? args =shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);?

??? if(!Runtime::Current()->IsStarted()) {?

?????UnstartedRuntime::Jni(self, method, receiver, args, result);?

??? } else {?

? ? ? ? InterpreterJni(self, method, shorty, receiver, args, result);?

??? }?

}?

????????再回調(diào)OAT的模式赴蝇,art_quick_invoke_stub/art_quick_invoke_static_stub最終會調(diào)用到art_quick_invoke_stub_internal(arch/arm/quick_entrypoints_arm.S)

art_quick_invoke_stub_internal(arch/arm/quick_entrypoints_arm.S)

ENTRYart_quick_invoke_stub_internal?

??? push??{r4, r5, r6, r7, r8, r9, r10, r11, lr}?????????????? @ spill regs?

??? .cfi_adjust_cfa_offset 16?

??? .cfi_rel_offset r4, 0?

??? .cfi_rel_offset r5, 4?

??? .cfi_rel_offset r6, 8?

??? .cfi_rel_offset r7, 12?

??? .cfi_rel_offset r8, 16?

??? .cfi_rel_offset r9, 20?

??? .cfi_rel_offset r10, 24?

??? .cfi_rel_offset r11, 28?

??? .cfi_rel_offset lr, 32?

??? mov???r11, sp???????????????????????? @save the stack pointer?

??? .cfi_def_cfa_register r11?


??? mov???r9, r3????????????????????????? @move managed thread pointer into r9?


??? add???r4, r2, #4????????????????????? @create space for method pointer in frame?

??? sub???r4, sp, r4????????????????????? @reserve & align *stack* to 16 bytes: native calling?

??? and???r4, #0xFFFFFFF0???????????????? @convention only aligns to 8B, so we have to ensure ART?

??? mov???sp, r4?????????? ???????????????@ 16B alignment ourselves.?


??? mov???r4, r0????????????????????????? @save method*?

??? add???r0, sp, #4????????????????????? @pass stack pointer + method ptr as dest for memcpy?

??? bl????memcpy????????????????????????? @memcpy (dest, src, bytes)?

??? mov???ip, #0????????????????????????? @set ip to 0?

??? str???ip, [sp]??????????????????????? @store null for method* at bottom of frame?


??? ldr???ip, [r11, #48]????????????????? @load fp register argument array pointer?

??? vldm??ip, {s0-s15}??????????????????? @copy s0 - s15?


??? ldr???ip, [r11, #44]????????????????? @load core register argument array pointer?

??? mov???r0, r4????????????????????????? @restore method*?

??? add???ip, ip, #4????????????????????? @skip r0?

??? ldm???ip, {r1-r3}???????????????????? @copy r1 - r3?

#ifdefARM_R4_SUSPEND_FLAG?

??? mov???r4, #SUSPEND_CHECK_INTERVAL???? @reset r4 to suspend check interval?

#endif?


??? ldr???ip, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]? @ get pointer to the code?

??? blx???ip????????????????????????????? @call the method?


??? mov???sp, r11???????????????????????? @restore the stack pointer?

??? .cfi_def_cfa_register sp?


??? ldr???r4, [sp, #40]?????????????????? @load result_is_float?

??? ldr???r9, [sp, #36]?????????????????? @load the result pointer?

??? cmp???r4, #0?

??? ite???eq?

??? strdeq r0, [r9]??????????????????????? @ store r0/r1 intoresult pointer?

??? vstrne d0, [r9]??????????????????????? @ store s0-s1/d0 intoresult pointer?


??? pop???{r4, r5, r6, r7, r8, r9, r10, r11, pc}?????????????? @ restore spill regs?

ENDart_quick_invoke_stub_internal?


????????找到ArtMethod的entry_point_from_quick_compiled_code_字段菩浙,這個就是EntryPointFromQuickCompiledCode,從而進入OAT函數(shù)執(zhí)行句伶。

#define ART_METHOD_QUICK_CODE_OFFSET_32 36?

ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32, art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())?

????????EntryPointFromQuickCompiledCode的初始化在class_linker的LoadClassMembers時調(diào)用的LinkCode劲蜻,有下面幾種類型:

????1,SetEntryPointFromQuickCompiledCode(GetQuickCode());// 這個是執(zhí)行OatMethod

????2考余,SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());?// ?Dex Method

????3先嬉,SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());// Native Method

????4楚堤,SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());? //?method->IsStatic() && !method->IsConstructor()

????????如果是強制使用了解釋器模式疫蔓,那么執(zhí)行的是代碼GetQuickToInterpreterBridge(non-static,

non-native)或GetQuickGenericJniStub(non-static, native)或GetQuickResolutionStub(static),這幾個EntryPoint對應的實際執(zhí)行函數(shù)如下身冬。

GetQuickGenericJniStub —?artQuickGenericJniTrampoline

GetQuickResolutionStub —?artQuickResolutionTrampoline

GetQuickToInterpreterBridge —?artQuickToInterpreterBridge

????????ArtMthod被Resolve之后衅胀,如果是走Oat模式就會執(zhí)行GetQuickCode。

????????樓上是EntryPointFromQuickCompiledCode的情況:

????????不同的執(zhí)行模式有不同的EntryPoint:

????1酥筝,解釋器-EntryPointFromInterpreter

????????在interpreter/interpreter_common.cc里會在執(zhí)行解釋器函數(shù)時滚躯,會獲得ArtMethod的Interpret EntryPoint執(zhí)行;

????2嘿歌,Jni -?EntryPointFromJni

????????interpreter/interpreter.cc掸掏,InterpreterJni函數(shù)會獲得ArtMethod的Jni EntryPoint執(zhí)行;

3搅幅,Oat-?EntryPointFromQuickCompiledCode

????????DexCache在Init的時候會將Method都初始化為ResolutionMethod阅束,這個Resolution Method是沒有dex method id的呼胚,是個RuntimeMethod茄唐,這是lazy load method,運行時resolve之后才會替換成實際的ArtMethod蝇更。

void DexCache::Init(const DexFile* dex_file, String*location, ObjectArray* strings,?ObjectArray* resolved_types, PointerArray*resolved_methods,?PointerArray* resolved_fields, size_t pointer_size) {?

? ? CHECK(dex_file != nullptr);?

? ? CHECK(location != nullptr);?

? ? CHECK(strings != nullptr);?

? ? CHECK(resolved_types != nullptr);?

? ? CHECK(resolved_methods != nullptr);?

? ? CHECK(resolved_fields != nullptr);?


?????SetDexFile(dex_file);?

?????SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);?

?????SetFieldObject(StringsOffset(), strings);?

?????SetFieldObject(ResolvedFieldsOffset(),resolved_fields);?

?????SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);?

?????SetFieldObject(ResolvedMethodsOffset(), resolved_methods);?


? ????Runtime* construntime = Runtime::Current();?

? ????if(runtime->HasResolutionMethod()) {?

? ? ? ? ? // Initialize the resolve methods array to contain trampolines for resolution.?

? ? ? ? ?Fixup(runtime->GetResolutionMethod(), pointer_size);?

? ????}?

}?


void DexCache::Fixup(ArtMethod* trampoline, size_tpointer_size) {?

? ? // Fix up the resolve methods array to contain trampoline for resolution.?

? ? CHECK(trampoline !=nullptr);?

? ? CHECK(trampoline->IsRuntimeMethod());?

? ? auto*resolved_methods = GetResolvedMethods();?

? ? for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i++) {?

??? ????if(resolved_methods->GetElementPtrSize(i, pointer_size) ==nullptr) {?

? ? ? ? ? ?resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);?

??? ????}?

? ? }?

}?

????????resolution method的EntryPointFromQuickCompiledCode指向GetQuickResolutionStub沪编,意思就是一開始,這些函數(shù)的執(zhí)行點都是從artQuickResolutionTrampoline開始年扩。

//Lazily resolve a method for quick. Called by stub code.?

extern"C" const void* artQuickResolutionTrampoline(ArtMethod* called, mirror::Object*receiver, Thread* self, ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {?

? ? ScopedQuickEntrypointChecks sqec(self);?

? ? // Start new JNI local reference state?

? ? JNIEnvExt* env = self->GetJniEnv();?

? ? ScopedObjectAccessUnchecked soa(env);?

? ? ScopedJniEnvLocalRefState env_state(env);?

? ? const char* old_cause = self->StartAssertNoThreadSuspension("Quick method resolution setup");?


? ? // Compute details about the called method(avoid GCs)?

? ? ClassLinker* linker = Runtime::Current()->GetClassLinker();?

? ? ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);?

? ? InvokeType invoke_type;?

? ? MethodReference called_method(nullptr, 0);?

? ? const bool called_method_known_on_entry = !called->IsRuntimeMethod();?

? ? if (!called_method_known_on_entry) {?

??? ????uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));?

??? ????const DexFile::CodeItem* code;?

??? ????called_method.dex_file = caller->GetDexFile();?

??? ????code = caller->GetCodeItem();?

??? ????CHECK_LT(dex_pc,code->insns_size_in_code_units_);?

??? ????const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);?

??? ????Instruction::Code instr_code = instr->Opcode();?

??? ????bool is_range;?

??? ????switch (instr_code) {?

? ? ? ? ? ? case Instruction::INVOKE_DIRECT:?

??????? ????????invoke_type = kDirect;?

????? ??????????is_range = false;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_DIRECT_RANGE:?

??????? ????????invoke_type = kDirect;?

??????? ????????is_range = true;?

???????????????break;?

? ? ? ? ? ? case Instruction::INVOKE_STATIC:?

??????? ????????invoke_type = kStatic;?

??????? ????????is_range = false;?

??????? ????????break;?

? ? ? ? ? ? ?case Instruction::INVOKE_STATIC_RANGE:?

??????? ????????invoke_type = kStatic;?

??????? ????????is_range = true;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_SUPER:?

??????? ????????invoke_type = kSuper;?

??????? ????????is_range = false;?

????? ??????????break;?

? ? ? ? ? ? case Instruction::INVOKE_SUPER_RANGE:?

??????? ????????invoke_type = kSuper;?

??????? ????????is_range = true;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_VIRTUAL:?

??????? ????????invoke_type = kVirtual;?

??????? ????????is_range = false;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_VIRTUAL_RANGE:?

??????? ????????invoke_type = kVirtual;?

??????? ????????is_range = true;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_INTERFACE:?

??????? ????????invoke_type = kInterface;?

??????? ????????is_range = false;?

??????? ????????break;?

? ? ? ? ? ? case Instruction::INVOKE_INTERFACE_RANGE:?

??????? ????????invoke_type = kInterface;?

??????? ????????is_range = true;?

??????? ????????break;?

? ? ? ? ? ? default:?

??????? ????????LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);?

??????? ????????UNREACHABLE();?

??? ????}?

? ? ? ? called_method.dex_method_index = (is_range)? instr->VRegB_3rc() : instr->VRegB_35c();?

? ? } else {?

??? ????invoke_type = kStatic;?

??? ????called_method.dex_file = called->GetDexFile();?

??? ????called_method.dex_method_index =called->GetDexMethodIndex();?

? }?

? uint32_t shorty_len;?

? const char* shorty = called_method.dex_file->GetMethodShorty(called_method.dex_file->GetMethodId(called_method.dex_method_index), &shorty_len);?

? RememberForGcArgumentVisitor visitor(sp,invoke_type == kStatic, shorty, shorty_len, &soa);?

? visitor.VisitArguments();?

?self->EndAssertNoThreadSuspension(old_cause);?

? const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;?

? // Resolve method filling in dex cache.?

? if (!called_method_known_on_entry) {?

? ? ? StackHandleScope<1> hs(self);?

? ? ? mirror::Object* dummy = nullptr;?

? ? ? HandleWrapperh_receiver(hs.NewHandleWrapper(virtual_or_interface? &receiver : &dummy));?

? ? ? DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);?

? ? ? called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);?

? }?

? const void* code = nullptr;?

? if (LIKELY(!self->IsExceptionPending())){?

? ? ? // Incompatible class change should have been handled in resolve method.?

? ? ?CHECK(!called->CheckIncompatibleClassChange(invoke_type)) << PrettyMethod(called) <<" " << invoke_type;?

? ? ? if (virtual_or_interface) {?

????? ????// Refine called method based on receiver.?

????? ????CHECK(receiver != nullptr) << invoke_type

????? ????ArtMethod* orig_called = called;?

????? ????if (invoke_type == kVirtual) {?

? ? ? ? ? ? ? called =receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));?

????? ????} else {?

? ? ? ? ? ? ? called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));?

????? ????}?

????? ????CHECK(called != nullptr) << PrettyMethod(orig_called) << " " << PrettyTypeOf(receiver) << " "

<< invoke_type << " " << orig_called->GetVtableIndex();

????? ????// We came here because of sharpening.Ensure the dex cache is up-to-date on the method index?

????? ????// of the sharpened method avoiding dirtying the dex cache if possible.?

????? ????// Note, called_method.dex_method_index references the dex method before the?

????? ????// FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares?

????? ????// about the name and signature.?

????????? uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();?

????? ????if(!called->HasSameDexCacheResolvedMethods(caller)) {?

? ? ? ? ? ? ? // Calling from one dex file to another, need to compute the method index appropriate to?

? ? ? ? ? ? ? // the caller's dex file. Since we get here only if the original called was a runtime?

? ? ? ? ? ? ? // method, we've got the correct dex_file and a dex_method_idx from above.?

? ? ? ? ? ? ? DCHECK(!called_method_known_on_entry);?

? ? ? ? ? ? ? DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);?

? ? ? ? ? ? ? const DexFile* caller_dex_file = called_method.dex_file;?

? ? ? ? ? ? ? uint32_tcaller_method_name_and_sig_index = called_method.dex_method_index;?

? ? ? ? ? ? ? update_dex_cache_method_index = called->FindDexMethodIndexInOtherDexFile(*caller_dex_file, caller_method_name_and_sig_index);?

????? ????}?

????? ????if ((update_dex_cache_method_index !=DexFile::kDexNoIndex) && (caller->GetDexCacheResolvedMethod(update_dex_cache_method_index,sizeof(void*)) != called)) {?

???????????????caller->SetDexCacheResolvedMethod(update_dex_cache_method_index,called, sizeof(void*));?

????? ????}?

? ? ? ? ? } else if (invoke_type == kStatic) {?

????? ????????const auto called_dex_method_idx = called->GetDexMethodIndex();?

????? ????????// For static invokes, we may dispatch to the static method in the superclass but resolve?

????? ????????// using the subclass. To prevent getting slow paths on each invoke, we force set the?

????? ????????// resolved method for the super class dex method index if we are in the same dex file.?

????? ????????// b/19175856?

????? ????????if (called->GetDexFile() == called_method.dex_file&& called_method.dex_method_index !=called_dex_method_idx) {?

? ? ? ? ? ? ? ? ?called->GetDexCache()->SetResolvedMethod(called_dex_method_idx,called, sizeof(void*));?

? ? ? ? ? ? }?

? ? ? ? }?


? ? ? ? // Ensure that the called method's class is initialized.?

? ? ? ? StackHandleScope<1>hs(soa.Self());?

??? ????Handlecalled_class(hs.NewHandle(called->GetDeclaringClass()));?

??? ????linker->EnsureInitialized(soa.Self(), called_class, true, true);?

??? ????if (LIKELY(called_class->IsInitialized())){?

? ? ? ? ? ? if(UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {?

??????? ????????// If we are single-stepping or the called method is deoptimized (by a?

??????? ????????// breakpoint, for example), then we have to execute the called method?

?? ?????????????// with the interpreter.?

??????? ????????code =GetQuickToInterpreterBridge();?

? ? ? ? ? ? } else if(UNLIKELY(Dbg::IsForcedInstrumentationNeededForResolution(self, caller))){?

??????? ????????// If the caller is deoptimized (by a breakpoint, for example), we have to?

??????? ????????// continue its execution with interpreter when returning from the called?

??????? ????????// method. Because we do not want to execute the called method with the?

??????? ????????// interpreter, we wrap its execution into the instrumentation stubs.?

?????? ?????????// When the called method returns, it will execute the instrumentation?

??????? ????????// exit hook that will determine the need of the interpreter with a call?

??????? ????????// toDbg::IsForcedInterpreterNeededForUpcall and deoptimize the stack if?

??????? ????????// it is needed.?

??????? ????????code =GetQuickInstrumentationEntryPoint();?

? ? ? ? ? ? } else {?

??????? ????????code =called->GetEntryPointFromQuickCompiledCode();?

? ? ? ? ? ? }?

??? ????} else if(called_class->IsInitializing()) {?

? ? ? ? ? ? if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self,called))) {?

??????? ????// If we are single-stepping or the called method is deoptimized (by a?

??????? ????// breakpoint, for example), then we have to execute the called method?

??????? ????// with the interpreter.?

??????? ????code =GetQuickToInterpreterBridge();?

? ? ? ? } else if (invoke_type == kStatic) {?

??????? ????// Class is still initializing, go too at and grab code (trampoline must be left in place?

??????? ????// until class is initialized to stop races between threads).?

???????????code = linker->GetQuickOatCodeFor(called);?

? ? ? ? } else {?

??????? ????// No trampoline for non-static methods.?

??????? ????code = called->GetEntryPointFromQuickCompiledCode();?

????? }?

??? } else {?

?????????DCHECK(called_class->IsErroneous());?

?? ?}?

? }?

? CHECK_EQ(code == nullptr,self->IsExceptionPending());?

? // Fixup any locally saved objects may havemoved during a GC.?

? visitor.FixupReferences();?

? // Place called method in callee-save frameto be placed as first argument to quick method.?

? *sp = called;?


? return code;?

}

????????上面代碼可知蚁廓,找到當前ArtMethod的流程大致的邏輯就是,根據(jù)caller函數(shù)ArtMethod的dex代碼厨幻,可以找到這個ArtMethod的函數(shù)調(diào)用類型(INVOKE_DIRECT相嵌,INVOKE_STATIC腿时,INVOKE_SUPER,INVOKE_VIRTUAL etc.)饭宾,不同的類型查找的方式不一樣批糟,比如Virtual Method要從虛表里找,Super Method要從父類的Method里去找看铆,找到之后調(diào)用ClassLinker的ResolveMethod來解析徽鼎,解析出來的ArtMethod的就是上面LinkCode過的ArtMethod。

????????下面就是ResolveMethod函數(shù)的實現(xiàn)弹惦,Class查找到Method否淤,之后在賦值到DexCache里,這樣下次再執(zhí)行就能直接找到Resolved Method棠隐。

ArtMethod*ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_tmethod_idx,?

?????????????????????????????????????Handle dex_cache,?

?????????????????????????????????????Handle class_loader,?

?????????????????????????????????????ArtMethod* referrer, InvokeType type) {?

? ? DCHECK(dex_cache.Get() != nullptr);?

? ? // Check for hit in the dex cache.?

? ? ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);?

? ? if (resolved != nullptr &&!resolved->IsRuntimeMethod()) {?

???????DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) <GetDexMethodIndex();?

??? ????return resolved;?

? ? }?

? ? // Fail, get the declaring class.?

? ? const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);?

? ? mirror::Class* klass = ResolveType(dex_file,method_id.class_idx_, dex_cache, class_loader);?

? ? if (klass == nullptr) {?

??? ????DCHECK(Thread::Current()->IsExceptionPending());?

??? ????return nullptr;?

? ? }?

? ? // Scan using method_idx, this saves string compares but will only hit for matching dex?

? ? // caches/files.?

? ? switch (type) {?

??? ????case kDirect:? // Fall-through.?

??? ????case kStatic:?

? ? ? ? ? ? resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx,image_pointer_size_);?

? ? ? ? ? ? DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);?

? ? ? ? ? ? break;?

??? ????case kInterface:?

? ? ? ? ? ? resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);?

? ? ? ? ? ? DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());?

? ? ? ? ? ? break;?

??? ????case kSuper:? // Fall-through.?

??? ????case kVirtual:?

? ? ? ? ? ? ?resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);?

? ? ? ? ? ? break;?

??? ????default:?

? ? ? ? ? ? LOG(FATAL) << "Unreachable -invocation type: " << type;?

? ? ? ? ? ? UNREACHABLE();?

? ? }?

? ? if (resolved == nullptr) {?

??? ????// Search by name, which works across dex files.?

??? ????const char* name = dex_file.StringDataByIdx(method_id.name_idx_);?

??? ????const Signature signature = dex_file.GetMethodSignature(method_id);?

??? ????switch (type) {?

? ? ? ? ? ? case kDirect:? // Fall-through.?

? ? ? ? ? ? case kStatic:?

??????? ????????resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);?

??????? ????????DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);?

??????? ????????break;?

? ? ? ? ? ? case kInterface:?

??????? ????????resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);?

??????? ????????DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());?

??????? ????????break;?

? ? ? ? ? ? case kSuper:? // Fall-through.?

? ? ? ? ? ? case kVirtual:?

??????? ????????resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);?

??????????????? break;?

? ? ? ? }?

? ? }?

? ? // If we found a method, check for incompatible class changes.?

? ? if (LIKELY(resolved != nullptr &&!resolved->CheckIncompatibleClassChange(type))) {?

??? ????// Be a good citizen and update the dex cache to speed subsequent calls.?

??? ????dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);?

??? ????return resolved;?

? ? } else {?

??? ????// If we had a method, it's anincompatible-class-change error.?

??? ????if (resolved != nullptr) {?

? ? ? ? ? ? ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);?

??? ????} else {?

? ? ? ? ? ? // We failed to find the method which means either an access error, an incompatible class?

? ? ? ? ? ? // change, or no such method. First try to find the method among direct and virtual methods.?

? ? ? ? ? ? const char* name = dex_file.StringDataByIdx(method_id.name_idx_);?

? ? ? ? ? ? const Signature signature = dex_file.GetMethodSignature(method_id);?

? ? ? ? ? ? switch (type) {?

? ? ? ? ? ? ? ? case kDirect:?

??????? ????????case kStatic:?

? ? ? ? ? ? ? ? ? ? resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);?

? ? ? ? ? ? ? ? ? ? // Note: kDirect and kStatic are also mutually exclusive, but in that case we would?

? ? ? ? ? ? ? ? ? ? //?????? have had a resolved method before, which triggers the "true" branch above.?

? ? ? ? ? ? ? ? ? ? break;?

??????? ????????case kInterface:?

??????? ????????case kVirtual:?

??????? ????????case kSuper:?

? ? ? ? ? ? ? ? ? ? resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);?

? ? ? ? ? ? ? ? ? ? break;?

? ? ? ? ? ? }?


? ? ? ? ? ? // If we found something, check that it can be accessed by the referrer.?

? ? ? ? ? ? bool exception_generated = false;?

? ? ? ? ? ? if (resolved != nullptr &&referrer != nullptr) {?

??????? ????????mirror::Class* methods_class =resolved->GetDeclaringClass();?

??????? ????????mirror::Class* referring_class =referrer->GetDeclaringClass();?

??????? ????????if(!referring_class->CanAccess(methods_class)) {?

?????????????????????ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, resolved, type);?

????????? ????????????exception_generated = true;?

??????? ????????} else if(!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) {?

?????????????????????ThrowIllegalAccessErrorMethod(referring_class, resolved);?

????????? ????????????exception_generated = true;?

??????? ????????}?

? ? ? ? ? ? }?

? ? ? ? ? ? if (!exception_generated) {?

??????? ????????// Otherwise, throw anIncompatibleClassChangeError if we found something, and check?

??????? ????????// interface methods and throw if we find the method there. If we find nothing, throw a?

??????? ????????// NoSuchMethodError.?

??????? ????????switch (type) {?

? ? ? ? ? ? ? ? ? ? case kDirect:?

? ? ? ? ? ? ? ? ? ? case kStatic:?

??????????? ????????????if (resolved != nullptr) {?

? ? ? ? ? ? ? ? ? ? ? ? ? ?ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);?

??????????? ????????????} else {?

? ? ? ? ? ? ? ? ? ? ? ? ? ? resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);?

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (resolved != nullptr) {?

????????? ??????????????????????ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);?

? ? ? ? ? ? ? ? ? ? ? ? ? ? } else {?

??????????????? ????????????????ThrowNoSuchMethodError(type, klass, name, signature);?

? ? ? ? ? ? ? ? ? ? ? ? ? ? }?

? ? ? ? ? ? ? ? ? ? ? ? }?

??????????? ????????????break;?

? ? ? ? ? ? ? ? ? ? case kInterface:?

??????????? ????????????if (resolved != nullptr) {?

?????????????????????????????ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);?

??????????? ????????????} else {?

????????????? ????????????????resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);?

????????????? ????????????????if (resolved != nullptr) {?

???????????????????????????????????ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);?

????????????? ????????????????} else {?

??????????????? ????????????????????ThrowNoSuchMethodError(type, klass, name, signature);?

???????????? ?????????????????}?

? ? ? ? ? ? ? ? ? ? ? ? ? }?

? ? ? ? ? ? ? ? ? ? ? ? ? break;?

????????? ????????????case kSuper:?

? ? ? ? ? ? ? ? ? ? ? ? ? if (resolved != nullptr) {?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);?

? ? ? ? ? ? ? ? ? ? ? ? ? } else {?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ThrowNoSuchMethodError(type, klass, name, signature);?

? ? ? ? ? ? ? ? ? ? ? ? ? }?

? ? ? ? ? ? ? ? ? ? ? ? ? break;?

????????? ????????????case kVirtual:?

? ? ? ? ? ? ? ? ? ? ? ? ? if (resolved != nullptr) {?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);?

? ? ? ? ? ? ? ? ? ? ? ? ? } else {?

?????????? ???????????????????resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);?

????????????? ????????????????if (resolved != nullptr) {?

???????????????????????????????????ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);?

????????????? ????????????????} else {?

??????????????? ????????????????????ThrowNoSuchMethodError(type, klass, name, signature);?

????????????? ????????????????}?

? ? ? ? ? ? ? ? ? ? ? ? ? }?

? ? ? ? ? ? ? ? ? ? ? ? ? break;?

? ? ? ? ? ? ? ? ? ? ? }?

????? ????????????}?

? ? ? ? ? ? ? }?

? ? ? ? ? ? ? ?Thread::Current()->AssertPendingException();?

??? ????????????return nullptr;?

? ? ? ? ? }?

}?

????????至此石抡,Art Method的執(zhí)行機制就算介紹完了,我們對整個函數(shù)執(zhí)行機制都有個全局的概念了助泽,包括:

????1汁雷,Art怎么進入第一個Method;

? ? 2报咳,ClassLinker在初始化的時候怎么加載成員函數(shù)(初始化幾個EntryPoint)侠讯;

????3,DexCache初始化的時候?qū)rtMethod初始化成Resolution Method暑刃,后續(xù)在運行時ResolveMethod厢漩;

4,解釋器模式在Art下是如何運行的岩臣;


1.4 Dalvik虛擬機運行機制

深入理解Dalvik虛擬機-解釋器的運行機制

http://blog.csdn.net/threepigs/article/details/51061926


????????Dalvik的指令執(zhí)行是解釋器+JIT的方式溜嗜,解釋器就是虛擬機來對Javac編譯出來的字節(jié)碼,做譯碼架谎、執(zhí)行炸宵,而不是轉(zhuǎn)化成CPU的指令集,由CPU來做譯碼谷扣,執(zhí)行土全。可想而知会涎,解釋器的效率是相對較低的裹匙,所以出現(xiàn)了JIT(Just In Time),JIT是將執(zhí)行次數(shù)較多的函數(shù)末秃,做即時編譯概页,在運行時刻,編譯成本地目標代碼练慕,JIT可以看成是解釋器的一個補充優(yōu)化惰匙。再之后又出現(xiàn)了Art虛擬機的AOT(Ahead Of Time)模式技掏,做靜態(tài)編譯,在Apk安裝的時候就會做字節(jié)碼的編譯项鬼,從而效率直逼靜態(tài)語言零截。

????????Java所有的方法都是類方法,因此Dalvik的字節(jié)碼執(zhí)行就兩種秃臣,一是類的Method涧衙,包括靜態(tài)和非靜態(tài),兩者的差距也就是有沒有this參數(shù)奥此,二就是類的初始化代碼弧哎,就是類加載的時候,成員變量的初始化以及顯式的類初始化塊代碼稚虎。

????????其中類的初始化代碼在dalvik/vm/oo/Class.cpp的dvmInitClass:

bool?dvmInitClass(ClassObject*?clazz)

{

????...

????dvmLockObject(self,?(Object*)clazz);

????...

????android_atomic_release_store(CLASS_INITIALIZING, (int32_t*)(void*)&clazz->status);

????dvmUnlockObject(self,?(Object*)clazz);

????...

????initSFields(clazz);

????/*?Execute?any?static?initialization?code.

?????*/

????method?=?dvmFindDirectMethodByDescriptor(clazz,?"",?"()V");

????if(method?==?NULL)?{

????????LOGVV("No??found?for?%s",?clazz->descriptor);

????}?else{

????????LOGVV("Invoking?%s.",?clazz->descriptor);

????????JValue?unused;

????????dvmCallMethod(self,?method,?NULL,?&unused);

????}

????...

}

????????從代碼可見撤嫩,類初始化的主要代碼邏輯包括:

? ? 1、類對象加鎖蠢终,所以類的加載是單線程的序攘;

? ? 2、初始化static成員(initSFields)寻拂;

? ? 3程奠、調(diào)用,靜態(tài)初始化塊祭钉;

????????類的初始化塊代碼在的成員函數(shù)里丁屎“崮校可見Dalvik的字節(jié)碼解釋,本質(zhì)上還是類成員函數(shù)的解釋執(zhí)行框杜。

????????虛擬機以Method作為解釋器的執(zhí)行單元拥坛,其入口就統(tǒng)一為dvmCallMethod塑陵,該函數(shù)的定義在dalvik/vm/interp/Stack.cpp里傻工。

void?dvmCallMethod(Thread*?self,?const?Method*?method,?Object*?obj, JValue*?pResult,?...)

{

????va_list?args;

????va_start(args,?pResult);

????dvmCallMethodV(self,?method,?obj,?false,?pResult,?args);

????va_end(args);

}

?void?dvmCallMethodV(Thread*?self,?constMethod*?method,?Object*?obj, bool?fromJni, JValue*?pResult, va_list?args)

{

????...

????if(dvmIsNativeMethod(method))?{

????????TRACE_METHOD_ENTER(self,?method);

????????/*

?????????*?Because?we?leave?no?space?for?local?variables,?"curFrame"?points

?????????*?directly?at?the?method?arguments.

? ? ? ? */

? ? ? ? (*method->nativeFunc)((u4*)self->interpSave.curFrame,?pResult, method,?self);

????????TRACE_METHOD_EXIT(self,?method);

????}?else{

????????dvmInterpret(self,?method,?pResult);

????}

????…

}

????????Java的Method有native函數(shù)和非native函數(shù)鲜戒,native的函數(shù)的代碼段是在so里,是本地指令集而非虛擬機的字節(jié)碼粟按。

????????虛擬機以Method作為解釋器的執(zhí)行單元诬滩,其入口就統(tǒng)一為dvmCallMethod,該函數(shù)的定義在dalvik/vm/interp/Stack.cpp里钾怔。

void?dvmCallMethod(Thread*?self,?constMethod*?method,?Object*?obj, JValue*?pResult,?...)

{

????va_list?args;

????va_start(args,?pResult);

????dvmCallMethodV(self,?method,?obj,?false,?pResult,?args);

????va_end(args);

}

?void?dvmCallMethodV(Thread*?self,?constMethod*?method,?Object*?obj, bool?fromJni, JValue*?pResult, va_list?args)

{

????...

????if(dvmIsNativeMethod(method))?{

????????TRACE_METHOD_ENTER(self,?method);

????????/*

?????????*?Because?we?leave?no?space?for?local?variables,"curFrame"points

?????????*?directly?at?the?method?arguments.

?????????*/

????????(*method->nativeFunc)((u4*)self->interpSave.curFrame,?pResult, method,?self);

????????TRACE_METHOD_EXIT(self,?method);

????}?else{

????????dvmInterpret(self,?method,?pResult);

????}

????…

}

????????如果method是個native的函數(shù)碱呼,那么就直接調(diào)用nativeFunc這個函數(shù)指針蒙挑,否則就調(diào)用dvmInterpret代碼宗侦,dvmInterpret就是解釋器的入口。

????????如果把Dalvik函數(shù)執(zhí)行的調(diào)用棧畫出來忆蚀,我們會更清楚整個流程矾利。

public?class?HelloWorld?{

????public?int?foo(int?i,?intj){

????????int?k?=?i?+?j;

????????return?k;

????}

? ??public?static?void?main(String[]?args)?{

????????System.out.print(new?HelloWorld().foo(1,?2));

????}

}

????????Dalvik虛擬機有兩個棧姑裂,一個Java棧,一個是VM的native棧男旗,vm的棧是OS的函數(shù)調(diào)用棧舶斧,Java的棧則是由VM管理的棧,每次在dvmCallMethod的時候察皇,在Method執(zhí)行之前茴厉,會調(diào)用dvmPushInterpFrame(java→java)或者dvmPushJNIFrame(java→native),JNI的Frame比InterpFrame少了局部變量的検踩伲空間矾缓,native函數(shù)的局部變量是在vm的native棧里,由OS負責壓棧出棧稻爬。DvmCallMethod結(jié)束的時候會調(diào)用dvmPopFrame做Java Stack的出棧嗜闻。

????????所以Java Method的執(zhí)行就是dvmInterpret函數(shù)對這個Method的字節(jié)碼做解析,函數(shù)的實參與局部變量都在Java的Stack里獲取桅锄。SaveBlock是StackSaveArea數(shù)據(jù)結(jié)構(gòu)琉雳,里面包含了當前函數(shù)對應的棧信息,包括返回地址等友瘤。而Native? Method的執(zhí)行就是Method的nativeFunc的執(zhí)行翠肘,實參和局部變量都是在VM的native stack里。

????????Method的nativeFunc是native函數(shù)的入口辫秧,dalvik虛擬機上的java的函數(shù)hook技術(shù)锯茄,都是通過改變Method的屬性,SET_METHOD_FLAG(method, ACC_NATIVE)茶没,偽裝成native函數(shù)肌幽,再設置nativeFunc作為鉤子函數(shù),從而實現(xiàn)hook功能抓半。很顯然喂急,hook了的method不再具有多態(tài)性。

????????nativeFunc的默認函數(shù)是dvmResolveNativeMethod(vm/Native.cpp):

void?dvmResolveNativeMethod(const?u4*?args,?JValue*?pResult,?const?Method*?method,?Thread *? self)

{

????ClassObject*?clazz?=?method->clazz;

????/*

?????*?If?this?is?a?static?method,?it?could?be?called?before?the?class

?????*?has?been?initialized.

?????*/

????if(dvmIsStaticMethod(method))?{

????????if(!dvmIsClassInitialized(clazz)?&&?!dvmInitClass(clazz))?{

????????????assert(dvmCheckException(dvmThreadSelf()));

????????????return;

????????}

????}?else{

????????assert(dvmIsClassInitialized(clazz)?|| dvmIsClassInitializing(clazz));

????}

????/*?start?with?our?internal-native?methods?*/

????DalvikNativeFunc?infunc?=?dvmLookupInternalNativeMethod(method);

????if(infunc?!=?NULL)?{

????????/*?resolution?always?gets?the?same?answer,?so?no?race?here?*/

????????IF_LOGVV()?{

????????????char*?desc?=?dexProtoCopyMethodDescriptor(&method->prototype);

????????????LOGVV("+++?resolved?native?%s.%s?%s,?invoking", clazz->descriptor,?method->name,?desc);

????????????free(desc);

????????}

????????if(dvmIsSynchronizedMethod(method))?{

????????????ALOGE("ERROR:?internal-native?can't?be?declared?'synchronized'");

????????????ALOGE("Failing?on?%s.%s",?method->clazz->descriptor,?method->name);

????????????dvmAbort();?????//?harsh,?but?this?is?VM-internal?problem

????????}

????????DalvikBridgeFunc?dfunc?=?(DalvikBridgeFunc)?infunc;

????????dvmSetNativeFunc((Method*)?method,?dfunc,?NULL);

????????dfunc(args,?pResult,?method,?self);

????????return;

????}

????/*?now?scan?any?DLLs?we?have?loaded?for?JNI?signatures?*/

????void*?func?=?lookupSharedLibMethod(method);

????if(func?!=?NULL)?{

????????/*?found?it,?point?it?at?the?JNI?bridge?and?then?call?it?*/

????????dvmUseJNIBridge((Method*)?method,?func);

????????(*method->nativeFunc)(args,?pResult,?method,?self);

????????return;

????}

????IF_ALOGW()?{

????????char*?desc?=?dexProtoCopyMethodDescriptor(&method->prototype);

????????ALOGW("No?implementation?found?for?native?%s.%s:%s", clazz->descriptor,?method->name,?desc);

????????free(desc);

????}

????dvmThrowUnsatisfiedLinkError("Native?method?not?found",?method);

}

????????dvmResolveNativeMethod首先會調(diào)用dvmLookupInternalNativeMethod查詢這個函數(shù)是否預置的函數(shù)笛求,主要是查下面的函數(shù)集:

static?DalvikNativeClass?gDvmNativeMethodSet[]?=?{

????{?"Ljava/lang/Object;",???????????????dvm_java_lang_Object,?0},

????{?"Ljava/lang/Class;",????????????????dvm_java_lang_Class,?0},

????{?"Ljava/lang/Double;",???????????????dvm_java_lang_Double,?0},

????{?"Ljava/lang/Float;",????????????????dvm_java_lang_Float,?0},

????{?"Ljava/lang/Math;",?????????????????dvm_java_lang_Math,?0},

????{?"Ljava/lang/Runtime;",??????????????dvm_java_lang_Runtime,?0},

????{?"Ljava/lang/String;",???????????????dvm_java_lang_String,?0},

????{?"Ljava/lang/System;",???????????????dvm_java_lang_System,?0},

????{?"Ljava/lang/Throwable;",????????????dvm_java_lang_Throwable,?0},

????{?"Ljava/lang/VMClassLoader;",????????dvm_java_lang_VMClassLoader,?0},

????{?"Ljava/lang/VMThread;",?????????????dvm_java_lang_VMThread,?0},

????{?"Ljava/lang/reflect/AccessibleObject;", dvm_java_lang_reflect_AccessibleObject,?0},

????{?"Ljava/lang/reflect/Array;",????????dvm_java_lang_reflect_Array,?0},

????{?"Ljava/lang/reflect/Constructor;", dvm_java_lang_reflect_Constructor,?0},

????{?"Ljava/lang/reflect/Field;",????????dvm_java_lang_reflect_Field,?0},

????{?"Ljava/lang/reflect/Method;",???????dvm_java_lang_reflect_Method,?0},

????{?"Ljava/lang/reflect/Proxy;",????????dvm_java_lang_reflect_Proxy,?0},

????{?"Ljava/util/concurrent/atomic/AtomicLong;", dvm_java_util_concurrent_atomic_AtomicLong,?0},

????{?"Ldalvik/bytecode/OpcodeInfo;",?????dvm_dalvik_bytecode_OpcodeInfo,?0},

????{?"Ldalvik/system/VMDebug;",??????????dvm_dalvik_system_VMDebug,?0},

????{?"Ldalvik/system/DexFile;",??????????dvm_dalvik_system_DexFile,?0},

????{?"Ldalvik/system/VMRuntime;",????????dvm_dalvik_system_VMRuntime,?0},

????{?"Ldalvik/system/Zygote;",???????????dvm_dalvik_system_Zygote,?0},

????{?"Ldalvik/system/VMStack;",??????????dvm_dalvik_system_VMStack,?0},

????{?"Lorg/apache/harmony/dalvik/ddmc/DdmServer;", dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0},

????{?"Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",

????????????dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal,?0},

????{?"Lorg/apache/harmony/dalvik/NativeTestTarget;",

????????????dvm_org_apache_harmony_dalvik_NativeTestTarget,?0},

????{?"Lsun/misc/Unsafe;",????????????????dvm_sun_misc_Unsafe,?0},

????{?NULL,?NULL,?0},

};

????????不是內(nèi)置的話廊移,就會加載so庫,查詢對應的native函數(shù)探入,查詢的規(guī)則就是我們熟知的了狡孔,com.xx.Helloworld.foobar對應com_xx_Helloworld_foobar。要注意的是蜂嗽,這個函數(shù)并不是nativeFunc苗膝,接下來的dvmUseJNIBridge調(diào)用里,dvmCallJNIMethod會作為nativeFunc植旧,這個函數(shù)主要需要將之前提到的java stack frame里的ins實參辱揭,轉(zhuǎn)譯成jni的函數(shù)調(diào)用參數(shù)离唐。xposed/dexposed就會自己設置自己的nativeFun、自己接管native函數(shù)的執(zhí)行问窃。

????????dvmInterpret是解釋器的代碼入口亥鬓,代碼位置在interp/Interp.cpp

void?dvmInterpret(Thread*?self,?const?Method*?method,?JValue*?pResult)

{

????InterpSaveState?interpSaveState;

????ExecutionSubModes?savedSubModes;

????.?.?.

????interpSaveState?=?self->interpSave;

????self->interpSave.prev?=?&interpSaveState;

????.?.?.

????self->interpSave.method?=?method;

????self->interpSave.curFrame?=?(u4*)?self->interpSave.curFrame;

????self->interpSave.pc?=?method->insns;

????.?.?.

????typedef?void(*Interpreter)(Thread*);

????Interpreter?stdInterp;

????if(gDvm.executionMode?==?kExecutionModeInterpFast)

????????stdInterp?=?dvmMterpStd;

#if?defined(WITH_JIT)

????else?if(gDvm.executionMode?==?kExecutionModeJit?|| gDvm.executionMode == kExecutionModeNcgO0 || gDvm.executionMode?==?kExecutionModeNcgO1)

????????stdInterp?=?dvmMterpStd;

#endif

????else

????????stdInterp?=?dvmInterpretPortable;

????//?Call?the?interpreter

????(*stdInterp)(self);

????*pResult?=?self->interpSave.retval;

????/*?Restore?interpreter?state?from?previous?activation?*/

????self->interpSave?=?interpSaveState;

#if?defined(WITH_JIT)

????dvmJitCalleeRestore(calleeSave);

#endif

????if(savedSubModes?!=?kSubModeNormal)?{

????????dvmEnableSubMode(self,?savedSubModes);

????}

}

????????Thread的一個很重要的field就是interpSave,是InterpSaveState類型的域庇,里面包含了當前函數(shù)嵌戈,pc,當前棧幀等重要的變量听皿,dvmInterpret一開始調(diào)用的時候就會初始化咕别。

????????Dalvik解釋器有兩個,一個是dvmInterpretPortable写穴,一個是 dvmMterpStd惰拱。兩者的區(qū)別在于,前者是從c++實現(xiàn)啊送,后者是匯編實現(xiàn)偿短。

????????dvmInterpretPortable是在vm/mterp/out/InterpC-portable.cpp中定義:

void?dvmInterpretPortable(Thread*?self)

{

????.?.?.

????DvmDex*?methodClassDex;?????//?curMethod->clazz->pDvmDex

????JValue?retval;

????/*?core?state?*/

????const?Method*?curMethod;????//?method?we're?interpreting

????const?u2*?pc;???????????????//?program?counter

????u4*?fp;?????????????????????//?frame?pointer

????u2?inst;????????????????????//?current?instruction

????/*?instruction?decoding?*/

????u4?ref;?????????????????????//?16?or?32-bit?quantity?fetched?directly

????u2?vsrc1,?vsrc2,?vdst;??????//?usually?used?for?register?indexes

????/*?method?call?setup?*/

????const?Method*?methodToCall;

????bool?methodCallRange;

????/*?static?computed?goto?table?*/

????DEFINE_GOTO_TABLE(handlerTable);

????/*?copy?state?in?*/

????curMethod?=?self->interpSave.method;

????pc?=?self->interpSave.pc;

????fp?=?self->interpSave.curFrame;

????retval?=?self->interpSave.retval;

????methodClassDex?=?curMethod->clazz->pDvmDex;

????.?.?.

????FINISH(0);??????????????????/*?fetch?and?execute?first?instruction?*/

????/*---?start?of?opcodes?---*/

????/*?File:?c/OP_NOP.cpp?*/

????HANDLE_OPCODE(OP_NOP)

????FINISH(1);

????OP_END

????/*?File:?c/OP_MOVE.cpp?*/

????HANDLE_OPCODE(OP_MOVE?/*vA,?vB*/)

????vdst?=?INST_A(inst);

????vsrc1?=?INST_B(inst);

????ILOGV("|move%s?v%d,v%d?%s(v%d=0x%08x)", (INST_INST(inst)?==?OP_MOVE)???""?:?"-object",?vdst,?vsrc1, kSpacing,?vdst,?GET_REGISTER(vsrc1));

????SET_REGISTER(vdst,?GET_REGISTER(vsrc1));

????FINISH(1);

????OP_END

????…..

}

????????解釋器的指令執(zhí)行是通過跳轉(zhuǎn)表來實現(xiàn),DEFINE_GOTO_TABLE(handlerTable)定義了指令Op的goto表馋没。FINISH(0)昔逗,則表示從第一條指令開始執(zhí)行。

#?define?FINISH(_offset)?{??????????????????????????????????????????????????\

????ADJUST_PC(_offset);?????????????????????????????????????????????????\

? ? inst?=?FETCH(0);????????????????????????????????????????????????????\

????if(self->interpBreak.ctl.subMode)?{????????????????????????????????\

????????dvmCheckBefore(pc,?fp,?self);???????????????????????????????????\

????}???????????????????????????????????????????????????????????????????\

????goto?*handlerTable[INST_INST(inst)];????????????????????????????????\

}

#define?FETCH(_offset)?????(pc[(_offset)])

????????FETCH(0)獲得當前要執(zhí)行的指令篷朵,通過查跳轉(zhuǎn)表handlerTable來跳轉(zhuǎn)到這條指令的執(zhí)行點勾怒,就是函數(shù)后面的HANDLE_OPCODE的定義。

????????后者是針對不同平臺做過優(yōu)化的解釋器声旺。dvmMterpStd會做匯編級的優(yōu)化笔链,dvmMterpStdRun的入口就是針對不同的平臺指令集,有對應的解釋器代碼腮猖,比如armv7 neon對應的代碼就在mterp/out/InterpAsm-armv7-a-neon.S鉴扫。

dvmMterpStdRun:

#define?MTERP_ENTRY1?\

????.save?{r4-r10,fp,lr};?\

????stmfd???sp!,?{r4-r10,fp,lr}?????????@?save?9regs

#define?MTERP_ENTRY2?\

????.pad????#4;?\

????sub?????sp,?sp,?#4??????????????????@?align?64

????.fnstart

????MTERP_ENTRY1

????MTERP_ENTRY2

????/*?save?stack?pointer,?add?magic?word?for?debuggerd?*/

????str?????sp,?[r0,?#offThread_bailPtr]??@?save?SP?for?eventual?return

????/*?set?up?"named"?registers,?figure?out?entry?point?*/

????mov?????rSELF,?r0???????????????????@?set?rSELF

????LOAD_PC_FP_FROM_SELF()??????????????@?load?rPC?and?rFP?from?"thread"

????ldr?????rIBASE,?[rSELF,?#offThread_curHandlerTable]?@?set?rIBASE

????.?.?.????

????/*?start?executing?the?instruction?at?rPC?*/

????FETCH_INST()????????????????????????@?load?rINST?from?rPC

????GET_INST_OPCODE(ip)?????????????????@?extract?opcode?from?rINST

????GOTO_OPCODE(ip)?????????????????????@?jump?to?next?instruction

????.?.?.

#define?rPC?????r4

#define?rFP?????r5

#define?rSELF???r6

#define?rINST???r7

#define?rIBASE??r8

????????非jit的情況下,先是FETCH_INST把pc的指令加載到rINST寄存器澈缺,之后GET_INST_OPCODE獲得操作碼and _reg, rINST, #255坪创,是把rINST的低16位給ip寄存器,GOTO_OPCODE跳轉(zhuǎn)到對應的地址姐赡。

#define GOTO_OPCODE(_reg)??????add???? pc, rIBASE, _reg, lsl #6

????????rIBASE 指向的curHandlerTable是跳轉(zhuǎn)表的首地址莱预,GOTO_OPCODE(ip)就將pc的地址指向該指令對應的操作碼所在的跳轉(zhuǎn)表地址。

static?Thread*?allocThread(intinterpStackSize)

#ifndef?DVM_NO_ASM_INTERP

????thread->mainHandlerTable?=?dvmAsmInstructionStart;

????thread->altHandlerTable?=?dvmAsmAltInstructionStart;

????thread->interpBreak.ctl.curHandlerTable?=?thread->mainHandlerTable;

#endif

????????可見dvmAsmInstructionStart就是跳轉(zhuǎn)表的入口项滑,定義在dvmMterpStdRun里依沮,你可以在這里找到所有的Java字節(jié)碼的指令對應的解釋器代碼。

????????比如new操作符對應的代碼如下,先加載Thread.interpSave.methodClassDex悉抵,這是一個DvmDex指針肩狂,隨后加載 DvmDex的pResClasses來查找類是否加載過摘完,如果沒加載過姥饰,那么跳轉(zhuǎn)到 LOP_NEW_INSTANCE_resolve去加載類,如果加載過孝治,就是類的初始化以及AllocObject的處理列粪。LOP_NEW_INSTANCE_resolve就是調(diào)用clazz的dvmResolveClass加載。

/*?------------------------------?*/

????.balign?64

????.L_OP_NEW_INSTANCE:?/*?0x22?*/

????/*?File:?armv5te/OP_NEW_INSTANCE.S?*/

????/*

?????*?Create?a?new?instance?of?a?class.

?????*/

????/*?new-instance?vAA,?class@BBBB?*/

????ldr?????r3,?[rSELF,?#offThread_methodClassDex]????@?r3<-?pDvmDex

????FETCH(r1,?1)????????????????????????@?r1<-?BBBB

????ldr?????r3,?[r3,?#offDvmDex_pResClasses]????@?r3<-?pDvmDex->pResClasses

????ldr?????r0,?[r3,?r1,?lsl?#2]????????@?r0<-?resolved?class

#if?defined(WITH_JIT)

????add?????r10,?r3,?r1,?lsl?#2@?r10<-?&resolved_class

#endif

????EXPORT_PC()?????????????????????????@?req'd?forinit,?resolve,?alloc

????cmp?????r0,?#0@?already?resolved?

????beq?????.LOP_NEW_INSTANCE_resolve?????????@?no,?resolve?it?now

????.LOP_NEW_INSTANCE_resolved:???@?r0=class

????ldrb????r1,?[r0,?#offClassObject_status]????@?r1<-?ClassStatus?enum

????cmp?????r1,?#CLASS_INITIALIZED??????@?has?classbeen?initialized?

????bne?????.LOP_NEW_INSTANCE_needinit????????@?no,?init?classnow

????.LOP_NEW_INSTANCE_initialized:?@?r0=class

????mov?????r1,?#ALLOC_DONT_TRACK???????@?flags?foralloc?call

????bl??????dvmAllocObject??????????????@?r0<-?newobject

????b???????.LOP_NEW_INSTANCE_finish??????????@?continue

????.LOP_NEW_INSTANCE_needinit:

????mov?????r9,?r0??????????????????????@?save?r0

????bl??????dvmInitClass????????????????@?initialize?class

????cmp?????r0,?#0??????????????????????@?check?booleanresult

????mov?????r0,?r9??????????????????????@?restore?r0

????bne?????.LOP_NEW_INSTANCE_initialized?????@?success,?continue

????b???????common_exceptionThrown??????@?failed,?deal?with?init?exception

????/*

?????*?Resolution?required.??This?is?the?least-likely?path.

?????*

?????*??r1?holds?BBBB

?????*/

????.LOP_NEW_INSTANCE_resolve:

????ldr?????r3,?[rSELF,?#offThread_method]?@?r3<-?self->method

????mov?????r2,?#0??????????????????????@?r2<-?false

????ldr?????r0,?[r3,?#offMethod_clazz]??@?r0<-?method->clazz

????bl??????dvmResolveClass?????????????@?r0<-?resolved?ClassObject?ptr

????cmp?????r0,?#0??????????????????????@?got?null?

????bne?????.LOP_NEW_INSTANCE_resolved????????@?no,?continue

????b???????common_exceptionThrown??????@?yes,?handle?exception??

2 參考鏈接

深入理解Dalvik虛擬機-解釋器的運行機制

http://blog.csdn.net/threepigs/article/details/51061926


深入理解ART虛擬機—ART的函數(shù)運行機制

http://blog.csdn.net/threepigs/article/details/52884904


Dalvik虛擬機的運行過程分析

http://shangxun.iteye.com/blog/2124440


深入理解Dalvik虛擬機-Android應用進程啟動過程分析

http://blog.csdn.net/threepigs/article/details/50779056


java virtual machine(java虛擬機的運行機制)谈飒?

http://webzixue.iteye.com/blog/1227802

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岂座,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杭措,更是在濱河造成了極大的恐慌费什,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件手素,死亡現(xiàn)場離奇詭異鸳址,居然都是意外死亡,警方通過查閱死者的電腦和手機泉懦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門稿黍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人崩哩,你說我怎么就攤上這事巡球。” “怎么了邓嘹?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵酣栈,是天一觀的道長。 經(jīng)常有香客問我汹押,道長钉嘹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任鲸阻,我火速辦了婚禮跋涣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸟悴。我一直安慰自己陈辱,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布细诸。 她就那樣靜靜地躺著沛贪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上利赋,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天水评,我揣著相機與錄音,去河邊找鬼媚送。 笑死中燥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的塘偎。 我是一名探鬼主播疗涉,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吟秩!你這毒婦竟也來了咱扣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤涵防,失蹤者是張志新(化名)和其女友劉穎闹伪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壮池,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡偏瓤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了火窒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硼补。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖熏矿,靈堂內(nèi)的尸體忽然破棺而出已骇,到底是詐尸還是另有隱情,我是刑警寧澤票编,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布褪储,位于F島的核電站,受9級特大地震影響慧域,放射性物質(zhì)發(fā)生泄漏鲤竹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一昔榴、第九天 我趴在偏房一處隱蔽的房頂上張望辛藻。 院中可真熱鬧,春花似錦互订、人聲如沸吱肌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氮墨。三九已至纺蛆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間规揪,已是汗流浹背桥氏。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猛铅,地道東北人字支。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像奕坟,于是被迫代替她去往敵國和親祥款。 傳聞我的和親對象是個殘疾皇子清笨,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理月杉,服務發(fā)現(xiàn),斷路器抠艾,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉苛萎,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評論 0 9
  • Jni數(shù)據(jù)類型 Jni方法 來自 http://blog.chinaunix.net/uid-22028680-i...
    FlyDragonInSky閱讀 912評論 0 0
  • 對你的愛腌歉,我不將就 文/沐浴歲月 One 現(xiàn)在才明白,原來有些故事齐苛,我們只能用來回憶翘盖,有些人,我們只能默默地想念凹蜂,...
    郭沐辰閱讀 535評論 6 13
  • 看庭前花開花落馍驯,榮辱不驚,望天上云亂云舒玛痊,去留無意汰瘫。相視而笑,莫逆于心呢擂煞! 互聯(lián)網(wǎng)拉近了人與人之間的距離混弥,也將“人...
    硯瀾閱讀 257評論 2 5