Dalvik
是Google
公司本身設計用於Android
平臺的Java
虛擬機,它是Android
平臺的重要組成部分,支持dex
格式的Java
應用程序的運行。html
Dalvik
做爲面向Linux
、爲嵌入式操做系統設計的虛擬機,主要負責完成 對象生命週期管理、堆棧管理、線程管理、安全和異常管理,以及垃圾回收等。Dalvik
充分利用Linux
進程管理的特定,對其進行了面向對象的設計,使得能夠 同時運行多個進程,而傳統的Java
程序一般只能運行一個進程。java
JVM
虛擬機基於 棧的結構,基於棧的指令更緊湊,使用的指令只佔用一個字節,於是成爲字節碼。Dalvik
虛擬機則是基於 寄存器,基於寄存器的指令因爲須要指定源地址和目標地址,所以須要佔用更多的指令空間,某些指令須要佔用兩個字節。Java
虛擬機運行的是Java
字節碼。Java
類會被編譯成一個或者多個.class
文件,而後打包到jar
文件中,接着Java
虛擬機會從相應的.class
文件和.jar
文件中獲取對應的字節碼。Dalvik
虛擬機運行的是.dex
文件。在Java
類被編譯成.class
文件後,還會經過dx
工具將全部的.class
文件轉換一個.dex
文件,Dalvik
虛擬機再從中讀取指令和數據。.dex
文件除了減小總體的文件尺寸和I/O
操做次數,也提升了類的查找速度。class
文件中包含多個不一樣的方法簽名,若是A
類文件引用B
類文件中的方法,方法簽名也會被複制到A
類文件中(在虛擬機加載類的鏈接階段將會使用該簽名連接到B
類的對應方法),也就是說,多個不一樣的類會同時包含相同的方法簽名,一樣地,大量的字符串常量在多個類文件中也被重複使用,這些冗餘信息會直接增長文件的體積,而JVM
在把描述類的數據從.class
文件加載到內存時,須要對數據進行校驗、轉換解析和初始化,最終才造成能夠被虛擬機直接使用的JAVA
類型,由於大量的冗餘信息,會嚴重影響虛擬機解析文件的效率。.dex
文件中,因爲dx
工具會對JAVA
類文件從新排列,將全部JAVA
類文件中的常量池分解,消除其中的冗餘信息,從新組合造成一個常量池,全部的類文件共享同一個常量池,使得相同的字符串、常量在.dex
文件中只出現一次,從而減少了文件的體積。dex
格式的字節碼,不兼容Java
字節碼格式32
位的索引12KB
2MB
,默認最大值爲16MB
1MB
,支持的最大值爲1024MB
-Xms
和-Xmx
修改.dex
文件結構和.class
文件結構差別的地方不少,但從攜帶的信息上看,.dex
和.class
文件是一致的:android
header
:存儲了各個數據類型的起始地址、偏移量等信息。proto_ids
:描述函數原型信息,包括返回值,參數信息。好比「test:()V」
methods_ids
:函數信息,包括所屬類及對應的proto
信息。雖然.dex
文件的結構很緊湊,但想要運行時的性能獲得進一步提高,還須要對dex
文件進行進一步優化。優化主要針對如下幾個方面:面試
.dex
文件中的全部類.dex
文件通過優化後文件大小會膨脹,大約增長到原來的1~4
倍。對於內置應用,通常在系統編譯後,便會生成優化文件odex(Optimized dex)
。一個Android
應用程序,須要通過如下過程才能夠在Dalvik
虛擬機上運行:安全
Java
源文件編譯成.class
文件dx
工具把.class
文件轉換成.dex
文件aapt
工具把.dex
文件、資源文件以及AndroidManifest.xml
文件組合成APKAPK
安裝到Android
設備運行一個dex
文件須要類加載器加載原生類和Java
類,而後經過解釋器根據指令集對Dalvik
字節碼進行解釋和執行。Dalvik
類加載器使用mmap
函數,將dex
文件映射到內存中,經過普通的內存讀取操做便可訪問dex
文件,而後解析dex
文件內容並加載其中的類到哈希表中。bash
總的來講,dex
文件能夠抽象爲三個部分:頭部、索引、數據。經過頭部能夠知道索引的位置和數目,以及數據區的起始位置。將dex
文件映射到內存後,Dalvik
會調用dexFileParse
函數對其進行分析,分析的結果放到DexFile
數據結構中。DexFile
中的baseAddr
指向映射區的起始位置,pClassDefs
指向class
索引的起始位置。爲了加快class
的查找速度,還建立一個哈希表,對class
名字進行哈希並生成索引。數據結構
解析工做完成後就進行class的加載,加載的類須要用ClassObject
數據結構來存儲。架構
typedef struct Object {
ClassObject* clazz; // 類型對象
Lock lock; // 鎖對象
} Object;
複製代碼
其中clazz
指向ClassObjec
t對象,還包含一個Lock
對象。若是其它線程想要獲取它的鎖,只有等這個線程釋放。Dalvik
每加載一個class
都會對應一個ClassObject
對象,加載過程會在內存中分配幾個區域,分別存放directMethod
、virtualMethod
、sfield
、ifield
。這些信息從dex
文件的數據區中讀取。字段Field
的定義以下:app
struct Field {
ClassObject* clazz; //所屬類型
const char* name; // 變量名稱
const char* signature; // 如「Landroid/os/Debug;」
u4 accessFlags; // 訪問標記
#ifdef PROFILE_FIELD_ACCESS
u4 gets;
u4 puts;
#endif
};
複製代碼
待獲得class
索引後,實際的加載由loadClassFromDex
來完成。首先它會讀取class
的具體數據,分別加載directMethod
、virtualMethod
、ifield
和sfield
,而後爲ClassObject
數據結構分配內存,並讀取dex
文件的相關信息。加載完成後,將加載的class
經過dvmAddClassToHash
函數放入哈希表,以方便下次查找;最後,經過dvmLinkClass
查找該類的超類,若是有接口類則加載相應的接口類。函數
對於任何虛擬機來講,解釋器無疑是核心的部分,全部的Java
字節碼都通過解釋器解釋執行。因爲Dalvik
解釋器的效率很重要,Android
分別實現了C
語言版和各類彙編語言版的解釋器。解釋器一般是循環執行,須要一個入口函數調用處理程序執行第一條指令,然後每條指令執行時引出下一條指令,經過函數指針調用處理程序。
在Android 4.4
以後,Google
開始使用了更加優秀的ART
虛擬機來替換Dalvik
虛擬機,下面咱們就來對比一下這二者之間的區別。
在 打包的過程當中 會先將.java
等源碼經過javac
編譯成.class
文件,再經過dx
將.class
文件轉換成Dalvik
虛擬機執行的.dex
文件。
在 應用啓動的時候 先將.dex
文件 轉換成機器碼,又由於65536
的文件,致使在應用冷啓動的時候有一個合包的過程,最後的結果就是app
的啓動時間有可能變慢,這就是Dalvik
虛擬機的JIT(Just in Time)
特性。
ART
除了兼容了Dalvik
虛擬機的特性以外,還有一個很好的特性AOT(Ahead of Time)
,這個特性就是把 .dex 文件轉換成機器碼 這個步驟提早到了 應用安裝 的時候,ART
虛擬機將.dex
文件轉換成可直接運行的.oat
文件,ART
虛擬機天生支持多dex
,因此也不會有一個合包的過程,所以會極大的提高APP
冷啓動速度。
優勢:
APP
冷啓動速度GC
速度Debug
特性缺點:
APP
安裝速度慢,由於在APK
安裝的時候要生成可運行.oat
文件APK
佔用空間大,由於在APK
安裝的時候要生成可運行.oat
文件理解 Android 虛擬機體系結構 Android Dalvik 虛擬機和 ART 虛擬機對比
Dalvik 虛擬機簡要介紹和學習計劃 Dalvik 虛擬機的啓動過程分析 Dalvik 虛擬機的運行過程分析
深刻理解 Dalvik 虛擬機 - Android應用進程啓動過程分析 深刻理解 ART 虛擬機 - 虛擬機的啓動 深刻理解 ART 虛擬機 - ART 的函數運行機制 深刻理解 Dalvik 虛擬機 - 解釋器的運行機制 深刻理解ART虛擬機 - ImageSpace的加載過程分析