Android虛擬機器與類載入機制

語言: CN / TW / HK

theme: cyanosis highlight: a11y-dark


JVM和Dalvik/ART

Android應用程式執行在Dalvik/ART虛擬機器,並且每一個應用程式對應有一個單獨的Dalvik虛擬機器例項。Dalvik虛擬機器實則也算是一個Java虛擬機器,只不過它執行的不是class檔案,而是dex檔案。Dalvik虛擬機器與Java虛擬機器共享有差不多的特性,差別在於兩者執行的指令集是不一樣的,前者的指令集是基本暫存器的,而後者的指令集是基於堆疊的。

基於棧的虛擬機器

對於基於棧的虛擬機器來說,每一個執行時的執行緒,都有一個獨立的棧。棧中記錄了方法呼叫的歷史,每有一次方法呼叫,棧中便會多一個棧楨。最頂部的棧楨稱作當前棧楨,其代表著當前執行的方法。基於棧的虛擬機器通過運算元棧進行所有操作。

Java public class Demo { public static void test() { int a = 1; int b = 2; int c = a + b; } }

使用javap -c xxx.class進行反編譯,檢視位元組碼指令:

  • iconst_1:將int型別常量a壓入運算元棧
  • istore_0:將int型別常量a存入區域性變數0
  • iadd:執行int型別的加法操作

執行過程如下:

基於暫存器的虛擬機器

暫存器是CPU的組成部分。暫存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、資料和位址。

基於暫存器的虛擬機器中沒有運算元棧,但是有很多虛擬暫存器。其實和運算元棧相同,這些暫存器也存放在執行時棧中,本質上就是一個數組。與JVM相似,在Dalvik VM中每個執行緒都有自己的PC和呼叫棧,方法呼叫的活動記錄以幀為單位儲存在呼叫棧上。

與JVM版相比,可以發現Dalvik版程式的指令數明顯減少了,資料移動次數也明顯減少了。

ART和Dalvik

Dalvik虛擬機器執行的是dex位元組碼,解釋執行。 從Android 2.2版本開始,支援JIT即時編譯(Just In Time)在程式執行的過程中進行選擇熱點程式碼(經常執行的程式碼)進行編譯或者優化。而ART(Android Runtime) 是在 Android 4.4 中引入的一個開發者選項,也是 Android 5.0 及更高版本的預設 Android 執行時。ART虛擬機器執行的是本地機器碼。 Android的執行時從Dalvik虛擬機器替換成ART虛擬機器,並不要求開發者將自己的應用直接編譯成目標機器碼,APK仍然是一個包含dex位元組碼的檔案。

dex2aot

Dalvik下應用在安裝的過程,會執行一次優化,將dex位元組碼進行優化生成odex檔案。而Art下將應用的dex位元組碼翻譯成本地機器碼的最恰當AOT時機也就發生在應用安裝的時候。ART 引入了預先編譯機制**(Ahead Of Time),在安裝時,ART 使用裝置自帶的 dex2oat 工具來編譯應用,dex中的位元組碼將被編譯成本地機器碼。

Android N(7.0)的運作方式

ART 使用預先 (AOT) 編譯,並且從 Android N混合使用AOT編譯,解釋和JIT。

1、最初安裝應用時不進行任何 AOT 編譯(安裝又快了),執行過程中解釋執行,對經常執行的方法進行JIT,經過 JIT 編譯的方法將會記錄到Profile配置檔案中。

2、當裝置閒置和充電時,編譯守護程序會執行,根據Profile檔案對常用程式碼進行 AOT 編譯。待下次執行時直接使用。

類載入機制

雙親委派機制

```Java protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //檢查類是否已經被載入過 Class<?> c = findLoadedClass(name); if (c == null) { //沒被載入過,就去載入該類 try { if (parent != null) { //parent不為null,則呼叫parent的loadClass去載入 c = parent.loadClass(name, false); } else { //parent為null,則呼叫BootClassLoader去載入類(從Framework中找) c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader }

            if (c == null) {
                //如果還是找不到,就呼叫findClass自己去找(從app中找)
                c = findClass(name);
            }
        }
        return c;
}

```

某個類載入器在載入類時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務或者沒有父類載入器時,才自己去載入。

好處:

1、避免重複載入,當父載入器已經載入了該類的時候,就沒有必要子ClassLoader再載入一次。

2、安全性考慮,防止核心API庫被隨意篡改。

類載入過程

關注木水小站 (zhangmushui.cn)和微信公眾號【木水Code】,及時獲取更多最新技術乾貨。