從JVM到Dalivk再到ART(class,dex,odex,vdex,ELF)

個人博客地址 http://dandanlove.com/

現在市面上的 Android 手機大部分都是運行的是ART虛擬機了。還記得自己一部 Android手機(HuaweiG520),Android4.1 系統。那時候還是沒有 ART虛擬機 的。作爲Android開發者,我們應該對 Android 的發展歷史有些瞭解爲什麼 Android 會經歷這麼多的變化。Android 是先有 JVM 然後是 Dalvik ,接着是現在的 ART虛擬機 。那麼他們之間有什麼關係呢?

Dalvik和ART

JVM就不用講述了大家都有了解,不瞭解的參見JVM百度百科

DalvikGoogle 公司自己設計用於 Android 平臺的虛擬機,是 Google 等廠商合作開發的 Android 移動設備平臺的核心組成部分之一。它可以支持已轉換爲 .dex 格式的 Java 應用程序的運行,.dex 格式是專爲Dalvik 設計的一種壓縮格式,適合內存和處理器速度有限的系統。

Dalvik和JVM的主要區別

首先通過介紹 Dalvik 的時候我們就知道 Dalvik 運行的是 dex 文件,而JVM 運行的是 class 文件。

Dalvik VM 是基於寄存器的架構,而 JVM 是棧機。所以 Dalvik VM 的好處是可以做到更好的提前優化(ahead-of-time optimization)。 另外基於寄存器架構的VM執行起來更快,但是代價是更大的代碼長度。

基於寄存器架構的虛擬機有這麼多的好處,爲什麼之前設計JAVA的程序員沒有采用呢,而是採用現在基於棧的架構開發的呢?

因爲基於棧的虛擬機也有它的優點,它不對 host 平臺的 寄存器數量 做假設,有利於移植到不懂的平臺,這也符合的Java跨平臺的特點。而Dalvik 虛擬機則不關心這些,因爲它本來就是爲 ARM 這樣的多寄存器平臺設計的,另外 Dalvik 被移植到 x86 機器上,即使 x86 這種寄存器少的平臺,寄存器架構的虛擬機也可以運行。

一般來說,基於堆棧的機器必須使用指令才能從堆棧上的加載和操作數據,因此,相對基於寄存器的機器,它們需要更多的指令才能實現相同的性能。但是基於寄存器機器上的指令必須經過編碼,因此,它們的指令往往更大。

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

我們可以查看Demo.java在JVM中的class和Dalvik的dex字節碼文件:
詳見:使用dx將class轉dex總結

bytecode

想要了解更多:基於棧的虛擬機 VS 基於寄存器的虛擬機

Dalvik在JVM上的優化

  • 在編譯時提前優化代碼而不是等到運行時
  • 虛擬機很小,使用的空間也小;被設計來滿足可高效運行多種虛擬機實例。
  • 常量池已被修改爲只使用32位的索引,以簡化解釋器
  • 標準Java字節碼實行8位堆棧指令,Dalvik使用16位指令集直接作用於局部變量。局部變量通常來自4位的“虛擬寄存器”區。這樣減少了Dalvik的指令計數,提高了翻譯速度。

Dalivk進化之ART

2014年6月25日,Android L 正式亮相於召開的谷歌I/O大會,Android L 改動幅度較大,谷歌將直接刪除 Dalvik ,代替它的是傳聞已久的 ART。在在Android系統4.4 提出,在 Android5.0之後完全棄用 dalvik 全部採用 art爲執行環境。

ART( Android Runtime

ART 的機制與 Dalvik 不同。在 Dalvik 下,應用每次運行的時候,字節碼都需要通過即時編譯器(just in time ,JIT)轉換爲機器碼,這會拖慢應用的運行效率,而在ART 環境中,應用在第一次安裝的時候,字節碼就會預先編譯成機器碼,使其成爲真正的本地應用。這個過程叫做預編譯(AOT,Ahead-Of-Time)。這樣的話,應用的啓動(首次)和執行都會變得更加快速。

ART的優缺點

優點:

  • 系統性能的顯著提升。
  • 應用啓動更快、運行更快、體驗更流暢、觸感反饋更及時。
  • 更長的電池續航能力。
  • 支持更低的硬件。

缺點:

  • 機器碼佔用的存儲空間更大,字節碼變爲機器碼之後,可能會增加10%-20(不過在應用包中,可執行的代碼常常只是一部分。比如最新的 Google+ APK 是 28.3 MB,但是代碼只有 6.9 MB。)
  • 應用的安裝時間會變長。

class、dex、odex、ELF相愛相殺

從執行文件上面進行分析的話,JVM 對應 class 文件,Dalivk 對應 odex 文件,而 ART 對應 oat 文件。

工具:javac, dx
.java——>.class——->.dex
.java 文件經過 javac 編譯器生成 .class 字節碼 再經過。dx 工具生成 .dex

爲了在 JVM 優化出一個 Dalivk 虛擬機,所以把 JVM 運行的 class 文件進行打包優化爲 dex 文件,但其本質還是和 class 文件一樣屬於字節碼文件。但是爲了每次啓動時都去掉從字節碼到機器碼的編譯過程,Google 又從 Dalivk 中優化出了 ART,在其安裝應用的時候將 dex 文件進行預處理生成可執行的 oat 文件。

JIT的引入

據說 Android 2.2 的虛擬機 dalvik 使用了 JIT 技術,使其運行速度快了5倍。

dalvik 解釋並執行程序,JIT 技術主要是對多次運行的代碼進行編譯,當再次調用時使用編譯之後的機器碼,而不是每次都解釋,以節約時間。5倍是測試程序測出的值,並不是說程序運行速度也能達到5倍,這是因爲測試程序有很多的重複調用和循環,而一般程序主要是順序執行的,而且它是一邊運行,一邊編譯,一開始的時候提速不多,所以真正運行程序速度提高不是特別明顯。

每啓動一個應用程序,都會相應地啓動一個 dalvik 虛擬機,啓動時會建立JIT 線程,一直在後臺運行。當某段代碼被調用時,虛擬機會判斷它是否需要編譯成機器碼,如果需要,就做一個標記,JIT 線程不斷判斷此標記,如果發現被設定就把它編譯成機器碼,並將其機器碼地址及相關信息放入 entry table 中,下次執行到此就跳到機器碼段執行,而不再解釋執行,從而提高速度。

odex(optimized dex)

因爲 apk 實際爲 zip 壓縮包,虛擬機每次加載都需要從 apk 中讀取classes.dex 文件,這樣會耗費很多的時間,而如果採用了 odex 方式優化的 dex 文件,他包含了加載 dex 必須的依賴庫文件列表,只需要直接加載而不需要再去解析。

Android N 之前,對於在 dalvik 環境中 使用 dexopt 來對 dex 字節碼進行優化生成 odex 文件最終存在手機的 data/dalvik-cache 目錄下,最後把 apk 文件中的 dex 文件刪除。

Android O 之後,odex 是從 vdex 這個文件中 提取了部分模塊生成的一個新的可執行二進制碼文件 , odexvdex 中提取後,vdex 的大小就減少了。

  • 第一次開機就會生成在 /system/app/<packagename>/oat/
  • 在系統運行過程中,虛擬機將其 從 /system/appcopy/data/davilk-cache/ 下;
  • odex + vdex = apk 的全部源碼 (vdex 並不是獨立於 odex 的文件 odex + vdex 才代表一個 apk );

AOT(Ahead-of-time)

ART 推出了預先 (AOT) 編譯,可提高應用的性能。ART 還具有比 Dalvik 更嚴格的安裝時驗證。在安裝時,ART 使用設備自帶的 dex2oat 工具來編譯應用。該實用工具接受 DEX 文件作爲輸入,並針對目標設備生成已編譯應用的可執行文件。之後打開 App 的時候,不需要額外的翻譯工作,直接使用本地機器碼運行,因此運行速度提高。

AOTart 的核心,oat 文件包含 oatdataoatexec。前者包含 dex 文件內容,後者包含生成的本地機器指令,從這裏看出 oat 文件回會比 dex 文件佔用更大的存儲空間。

因爲 oat 文件包含生成的本地機器指令進而可以直接運行,它同樣保存在手機的 data/dalvik-cache 目錄下 PMS(PackgetManagerService)—>installd(守護進程)——>dex2oat(/system/bin/dex2oat) 。注意存放在 data/dalvik-cache 目錄下的後綴名都仍爲 .dex 前者其實表示一個優化過的 .dex 文件 後者爲 .art 文件。

push 一個新的 apk 文件覆蓋之前 /system/appapk 文件,會觸發 PKMS 掃描時下發 force_dex flag ,強行生成新的 vdex文件 ,覆蓋之前的vdex 文件,由於某種機制,這個新 vdex 文件會 copy/data/dalvik-cache/ 下,於是 art 文件也變化了。

混合運行時

Android N 開發者預覽版包含了一個混合模式的運行時。應用在安裝時不做編譯,而是解釋字節碼,所以可以快速啓動。ART中有一種新的、更快的解釋器,通過一種新的 JIT 完成,但是這種 JIT 的信息不是持久化的。取而代之的是,代碼在執行期間被分析,分析結果保存起來。然後,當設備空轉和充電的時候,ART 會執行鍼對“熱代碼”進行的基於分析的編譯,其他代碼不做編譯。爲了得到更優的代碼,ART 採用了幾種技巧包括深度內聯。

對同一個應用可以編譯數次,或者找到變“熱”的代碼路徑或者對已經編譯的代碼進行新的優化,這取決於分析器在隨後的執行中的分析數據。這個步驟仍被簡稱爲 AOT,可以理解爲“全時段的編譯”(All-Of-the-Time compilation)。

這種混合使用 AOT、解釋、JIT 的策略的全部優點如下。

  • 即使是大應用,安裝時間也能縮短到幾秒;
  • 系統升級能更快地安裝,因爲不再需要優化這一步;
  • 應用的內存佔用更小,有些情況下可以降低 50%;
  • 改善了性能;
  • 更低的電池消耗;

vdex

官網回答:ART的運作方式
dex2oat 工具接受一個 APK 文件,並生成一個或多個編譯工件文件,然後運行時將會加載這些文件。文件的個數、擴展名和名稱會因版本而異。
在 Android O 版本中,將會生成以下文件:

  • .vdex:其中包含 APK 的未壓縮 DEX 代碼,另外還有一些旨在加快驗證速度的元數據。
  • .odex:其中包含 APK 中已經過 AOT 編譯的方法代碼。
  • .art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內部表示,用於加快應用啓動速度。
  • 第一次開機就會生成在 /system/app/<packagename>/oat/ 下;
  • 在系統運行過程中,虛擬機將其 從 /system/appcopy/data/davilk-cache/ 下。

ELF文件

ELF(Executable and Linking Format)是一種對象文件的格式,用於定義不同類型的對象文件(Object files)中都放了什麼東西、以及都以什麼樣的格式去放這些東西。它自最早在 System V系統上出現後,被 xNIX 世界所廣泛接受,作爲缺省的二進制文件格式來使用。可以說,ELF 是構成衆多 xNIX 系統的基礎之一。

apk安裝過程

大家都知道 apk 其實就是 zipapk 安裝過程其實就是解壓過程。
用戶應用安裝涉及以下幾個目錄:

  • data/app 安裝目錄 安裝時會把 apk 文件 copy 到這裏;
  • data/dalvik-cache 如上述描述中的存放.dex ( .odex 無論 davilkdex 還是 artoat 格式);
  • data/data/pkg/ 存放應用程序的數據;

Android5.1 版本下 oat 文件都以 .dex 文件在 data/dalvik-cache 目錄下:

dalvik-cache.jpeg

Android8.0 版本下 dex2oat 工具生成的三個.art,.odex,.vdex文件都在 data/dalvik-cache 目錄下:

sogou-dalvik-cache-arm64.png

Android8.0 版本下搜狗輸入法在 data/app/com.sohu.inputmethod.sogou.xiaomi-JAKuH_Jhlk6Y36zKoTUN8Q== 目錄下:

sogou-data-app-xxxxx.jpg

Android8.0 版本下搜狗輸入法在 system/app/SogouInput 目錄下:

sogou-system-app-xxx.jpg

文中部分內容摘自:
深入理解JVM-字節碼執行引擎
知乎-Dalvik 虛擬機和 Sun JVM 在架構和執行方面有什麼本質區別?
《Java虛擬機原理圖解》4.JVM機器指令集
Android art-Android dex,odex,oat,vdex,art文件結構學習總結
Android N 混合使用 AOT 編譯,解釋和 JIT 三種運行時

修改歷史:
【第一次發佈】2017.09.18 19:00
【第二次主要修改不oat、vdex和odex相關內容】2020.04.02 22:00

文章到這裏就全部講述完啦,若有其他需要交流的可以留言哦

想閱讀作者的更多文章,可以查看我 個人博客 和公共號:
振興書城

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章