Android虛擬機與類加載機制

JVM和Dalvik/ART

Android應(yīng)用程序運行在Dalvik/ART虛擬機,并且每一個應(yīng)用程序?qū)?yīng)有一個單獨的Dalvik虛擬機實例。Dalvik虛擬機實則也算是一個Java虛擬機,只不過它執(zhí)行的不是class文件,而是dex文件。Dalvik虛擬機與Java虛擬機共享有差不多的特性,差別在于兩者執(zhí)行的指令集是不一樣的,前者的指令集是基本寄存器的,而后者的指令集是基于堆棧的。

基于棧的虛擬機

對于基于棧的虛擬機來說,每一個運行時的線程,都有一個獨立的棧。棧中記錄了方法調(diào)用的歷史,每有一次方法調(diào)用,棧中便會多一個棧楨。最頂部的棧楨稱作當(dāng)前棧楨,其代表著當(dāng)前執(zhí)行的方法。基于棧的虛擬機通過操作數(shù)棧進行所有操作。

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

使用javap -c xxx.class進行反編譯,查看字節(jié)碼指令:

  • iconst_1:將int類型常量a壓入操作數(shù)棧
  • istore_0:將int類型常量a存入局部變量0
  • iadd:執(zhí)行int類型的加法操作

執(zhí)行過程如下:

基于寄存器的虛擬機

寄存器是CPU的組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數(shù)據(jù)和位址。

基于寄存器的虛擬機中沒有操作數(shù)棧,但是有很多虛擬寄存器。其實和操作數(shù)棧相同,這些寄存器也存放在運行時棧中,本質(zhì)上就是一個數(shù)組。與JVM相似,在Dalvik VM中每個線程都有自己的PC和調(diào)用棧,方法調(diào)用的活動記錄以幀為單位保存在調(diào)用棧上。

與JVM版相比,可以發(fā)現(xiàn)Dalvik版程序的指令數(shù)明顯減少了,數(shù)據(jù)移動次數(shù)也明顯減少了。

ART和Dalvik

Dalvik虛擬機執(zhí)行的是dex字節(jié)碼,解釋執(zhí)行。 從Android 2.2版本開始,支持JIT即時編譯(Just In Time)在程序運行的過程中進行選擇熱點代碼(經(jīng)常執(zhí)行的代碼)進行編譯或者優(yōu)化。而ART(Android Runtime) 是在 Android 4.4 中引入的一個開發(fā)者選項,也是 Android 5.0 及更高版本的默認(rèn) Android 運行時。ART虛擬機執(zhí)行的是本地機器碼。 Android的運行時從Dalvik虛擬機替換成ART虛擬機,并不要求開發(fā)者將自己的應(yīng)用直接編譯成目標(biāo)機器碼,APK仍然是一個包含dex字節(jié)碼的文件。

dex2aot

Dalvik下應(yīng)用在安裝的過程,會執(zhí)行一次優(yōu)化,將dex字節(jié)碼進行優(yōu)化生成odex文件。而Art下將應(yīng)用的dex字節(jié)碼翻譯成本地機器碼的最恰當(dāng)AOT時機也就發(fā)生在應(yīng)用安裝的時候。ART 引入了預(yù)先編譯機制**(Ahead Of Time),在安裝時,ART 使用設(shè)備自帶的 dex2oat 工具來編譯應(yīng)用,dex中的字節(jié)碼將被編譯成本地機器碼。

Android N(7.0)的運作方式

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

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

2、當(dāng)設(shè)備閑置和充電時,編譯守護進程會運行,根據(jù)Profile文件對常用代碼進行 AOT 編譯。待下次運行時直接使用。

類加載機制

雙親委派機制

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

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

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

好處:

1、避免重復(fù)加載,當(dāng)父加載器已經(jīng)加載了該類的時候,就沒有必要子ClassLoader再加載一次。

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

類加載過程

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

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

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

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