1 概論
1.1 JVM概念
???????JVM是Java Virtual Machine的簡稱,本質上就是一個軟件,是計算機硬件的一層軟件抽象,在這之上才能夠運行Java程序,JAVA在編譯后會生成類似于匯編語言的JVM字節碼,與C語言編譯后產生的匯編語言不同的是,C編譯成的匯編語言會直接在硬件上跑,但JAVA編譯后生成的字節碼是在JVM上跑,需要由JVM把字節碼翻譯成機器指令,才能使JAVA程序跑起來。
???????JVM運行在操作系統上,屏蔽了底層實現的差異,從而有了JAVA吹噓的平臺獨立性和Write Once Run Anywhere。根據JVM規范實現的具體虛擬機有幾十種,主流的JVM包括Hotspot、Jikes RVM等,都是用C/C++和匯編編寫的,每個JRE編譯的時候針對每個平臺編譯,因此下載JRE(JVM、Java核心類庫和支持文件)的時候是分平臺的,JVM的作用是把平臺無關的.class里面的字節碼翻譯成平臺相關的機器碼,來實現跨平臺。
1.2 DVM概念
???????DVM是Dalvik Virtual Machine的縮寫,是Android4.4及以前使用的虛擬機,所有android程序都運行在android系統進程里,每個進程對應著一個Dalvik虛擬機實例。JVM和DVM都提供了對象生命周期管理,堆棧管理,線程管理,安全和異常管理及垃圾回收等重要功能,各自擁有一套完整的指令系統。
???????APK在打包的過程中會先將java等源碼通過javac編譯成.class文件,然后通過android的dx工具將.class文件轉換成Dalvik虛擬機能夠執行的.dex文件。Dalvik虛擬機在應用程序啟動時,會將.dex文件轉換成快速運行的機器碼進行執行。
1.3 ART概念
???????在android5.0及后續版本中,Google采用了ART正式取代了以往的Dalvik虛擬機,ART虛擬機會將.dex文件轉化為可直接運行的.oat文件。
1.3.1 JIT
???????JIT是Just In Time縮寫,稱為及時編譯技術。以JVM為例,javac將源程序編譯成java字節碼,JVM通過逐條解釋將字節碼翻譯成對應的機器指令,逐條讀入,逐條解釋翻譯,執行速度必然比c/c++編譯后的可執行二進制字節碼程序慢,為了提高執行速度,就引入了JIT技術,JIT會在運行時分析應用程序代碼,識別哪些方法可以歸類為熱方法,這些方法會被JIT編譯器編譯成對應的匯編代碼,然后存儲到代碼緩存中,以后調用這些方法時就不用解釋執行了,可以直接使用代碼緩存中已編譯好的匯編代碼,這能顯著提升應用程序的執行效率。Dalvik虛擬機在android2..2中增加了JIT。
1.3.2 AOT
AOT指的是預編譯技術,其全稱為Ahead Of Time。AOT就是在我們安裝APK時就將dex文件直接處理成可直接供ART虛擬機使用的機器碼。
1.4 APK的執行流程
引用一張Google的圖來看一下Android對apk的執行流程(圖片從上往下閱讀):
???????Java文件通過javac編譯成.class文件,然后通過SDK中的dx工具將.class文件轉換成Dalvik虛擬機能夠執行的.dex文件,然后與Native code(JNI)和資源文件一起打包成apk,apk安裝到手機后解壓出.dex文件,Dalvik虛擬機會通過dexopt工具將.dex文件優化,形成Odex文件,ART則會將.dex文件通過dex2oat工具編譯得到一個ELF文件,這是一個可執行文件。
2 虛擬機特性對比
2.1 JVM與DVM對比
-
Java虛擬機運行的是java字節碼,Dalvik虛擬機運行的是Dalvik字節碼
???????java程序經過編譯,生成java字節碼保存在.class文件中,JVM通過解碼.class文件中的內容來運行程序。而DVM運行的是Dalvik字節碼,所有的Dalvik字節碼都是由Java字節碼轉換而來,并打包到.dex(Dalvik Executable)可執行文件中,DVM通過解釋.dex文件來執行這些字節碼。
? -
Dalvik可執行文件體積更小
???????android SDK中有個dx工具,該工具負責將Java字節碼轉換為Dalvik字節碼,dx工具對Java類文件重新排列,將所有Java類文件中的常量池分解,消除其中冗余信息,重新組合形成一個常量池,所有的類文件共享同一個常量池,使得相同的字符串、常量在DEX文件中只出現一次,從而減少了文件的體積。 -
JVM基于棧,DVM基于寄存器
關于棧式虛擬機:
- 代碼必須使用這些指令來移動變量(即push和pop)
- 代碼尺寸小和解碼效率會更高些
- 堆棧虛擬機指令有隱含的操作數。
關于寄存器式虛擬機:
- 使用堆棧來分配激活記錄器
- 基于寄存器代碼免去了使用push和pop命令的麻煩,減少了每個函數的指令總數。
- 代碼尺寸和解碼效率不如基于棧虛擬機,因為它包含操作數,所以指令大于基于堆棧的指令。但是基于寄存器產生更少的代碼,所以總的代碼數不會增加。
- 寄存器虛擬機必須從操作指令中解碼操作數,需要額外的解碼操作。
因而,籠統說可以有以下結論:
- 要追求盡量實現簡單:選擇基于棧
- 傳輸代碼的大小盡量小:選擇基于棧
- 純解釋執行的解釋器的速度:選擇基于寄存器
- 帶有JIT編譯器的執行引擎的速度:隨便,兩者一樣;對簡易JIT編譯器而言基于棧的指令集可能反而更便于生成更快的代碼,而對比較優化的JIT編譯器而言輸入是基于棧還是基于寄存器都無所謂,經過parse之后就變得完全一樣了。
2.2 DVM與ART對比
-
ART采用AOT技術替代了JIT
???????ART在應用程序安裝時,就已經將所有的字節碼編譯成機器碼,運行的時候直接運行的是機器碼。而Dalvik則是在應用程序運行時,實時地將字節碼編譯成機器碼。因此,ART與Dalvik相比,省去了運行時將字節碼編譯成機器碼的過程,極大地提升了應用程序的運行效率。
???????雖然運行效率得到提升,但同時帶來了其它缺點:
- 安裝時需要將字節碼轉換成機器碼,因此ART需要更大的存儲空間
- 安裝時需要更多的時間
提高了垃圾回收效率
???????Dalvik虛擬機在GC時,會掛起虛擬機內部的所有線程,然后GC查找所有可回收的對象進行回收,回收后恢復所有掛起的線程。GC與應用程序的運行并不是并發執行的,如果GC頻繁或者GC時間過長都會導致應用程序運行卡頓。
???????ART對GC做了一定的改善,Dalvik的GC操作與應用程序是同步執行的(非并發),而ART則將原來的非并發過程改成部分并發,縮短了GC時間,提升了垃圾回收效率。提高了內存使用率、減少了內存碎片化
???????Dalvik虛擬機垃圾回收采用了Mark and Sweep算法,即“標記-清除”算法,這種算法先對需要回收的內存區域進行標記,然后再統一清除,它是一種比較高效的算法,但是帶來的弊端是會造成可用內存塊的不連續,碎片化嚴重。虛擬機多次gc之后,本來連續的內存區域變得千瘡百孔,以后為對象分配內存尋址會越來越難。
???????而ART在內存分配上做了優化,比如它開辟了一塊名為Large Object Space的內存區域,專門用來存放大對象,同時還引入了一個名為moving collector的技術,專門用來將gc后不連續的物理內存塊對齊,解決了Dalvik上內存碎片化嚴重的問題。
參考文章
https://blog.csdn.net/qq_27688259/article/details/82252855
http://www.lxweimin.com/p/5c00dd2bece6
http://www.lxweimin.com/p/1f779586efdc
https://blog.csdn.net/k1457047898/article/details/53471951