Dalvik和ART
這是我第三次寫Dalvik(以下簡稱DVM)和ART虛擬機了,它們都是Android手機上運行java代碼的虛擬虛擬機。DVM不是JVM,主要還是因為DVM的實現沒有遵守JVM的實現規范。
DVM與JVM基于的架構不同
JVM是基于棧的,當它需要到棧中去讀寫數據時,所需的指令就因此而增多,將導致速度變慢。手機的使用要求就是要快,顯然JVM是不能滿足這一性能要求。所以google就用Dalvik來解決這個性能問題。DVM是基于寄存器,因此它不需要像JVM那樣在復制數據時要使用大量的出入棧指令。不過因為DVM由于顯式指定了操作數,因此DVM基于寄存器的指令會比基于棧的指令要大,同時也因此減少了指令的數量。
DVM與JVM執行字節碼的方式不同
在Java SE程序中,Java類被編譯成一個或多個.class文件,并打包成.jar文件,程序運行時,JVM會通過相應的.class文件和jar文件獲取相應的字節碼。它的順序是.java文件->.class文件->.jar文件。
.jar文件里面包含多個.class文件,每個.class文件里面包含了該類的常量池、類信息、屬性等,當JVM加載該.jar文件時,會加載里面所有的.class文件,JVM這種加載方式很慢,對于內存有限制的移動設備來說非常不合適的。
而DVM則是用dx工具將所有的.class文件轉換為一個.dex文件,當應用程序運行時,DVM會從該.dex文件讀取指令和數據。它的順序是.java文件->.class文件->.dex文件。apk文件里面只包含了一個.dex文件,這個.dex文件將所有的.class文件里面所包含的信息全部都整合在一起了。這樣加載速度會快很多。.dex文件由類加載器處理,接著解釋器根據指令集對Dalvik字節碼進行解釋、執行,最后交給Linux處理。另外,.class文件會有很多冗余信息,dex工具會去除冗余信息,并把所有.class文件整合到.dex文件中,可以減少I/O操作,加快了類的查找速度。
一個應用程序對應一個DVM實例
在Android中的每一個應用都運行在一個DVM實例中,而每一個DVM實例都運行在一個獨立的進程空間中,獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉。
DVM是由Zygote創建和初始化
Zygote進程本身也是一個DVM進程。它同時也是用來創建和初始化DVM實例的。每當系統需要創建一個應用程序時,Zygote進程就fork自身(其就是復制自己,用了寫時復制),快速地創建和初始化一個DVM實例,用于應用程序的運行。對于一些只讀的系統庫,所有的DVM實例都會和Zygote共享一塊內存區域,從而節省了內存開銷。
DVM與JIT(即時編譯)
DVM還沒有加入JIT功能時,DVM每次執行代碼,都需要通過解釋器將.dex編譯成機器碼,然后交給系統處理,但效率不高。為了提高效率,在Android2.2后就加入了JIT,它會對多次運行的代碼(熱點代碼)進行編譯,生成相當精簡的本地機器碼,這樣下次執行到相同邏輯時,就可以直接使用編譯之后的本地機器碼,而不是每次都需要編譯。不過,需要注意的是:應用程序每一次重新運行的時候,都需要重新做這個編譯工作 ,因此每次重新打開應用程序,都需要JIT編譯。
DVM的運行時堆
DVM的運行堆使用標記-清除算法進行GC,由兩個Space和多個輔助數據結構組成。兩個Space分別是Zygote Space(Zygote Heap)和Allocation Space(Active Heap)。Zygote Space用來管理Zygote進程在啟動過程中預加載和創建的各種對象,它不會觸發GC。在Zygote進程和應用程序之間會共享Zygote Space。在Zygote進程fork第一個子進程之前,會把Zygote Space分成兩個部分,原來的已經被使用的那部分仍舊叫Zygote Space,而未使用那一個部分則就叫Allocation Space,以后的對象都會在Allocation Space上進行分配和釋放。Allcation Space不是進程間共享的,在每個進程中都獨立擁有一份。除了這兩個Space,還有以下的輔助數據結構:
Card Table:當第一個次進程垃圾標記后,記錄垃圾信息
Heap Bitmap:有兩個Heap Bitmap,一個用來記錄上次GC存活的對象 ,另一個用來記錄這次GC存活的對象。
Mark Stack:在GC標記的階段使用,它用來遍歷存活象。
ART的時代
在Android4.4時,發布了ART虛擬機,用來替代Dalvik虛擬機,但anroid4.4上默認還是運行Dalvik。Android 5.0后才默認使用ART虛擬機。
ART與DVM的區別
DVM是為32位CPU設計的(注定會被淘汰),而ART支持64位并且兼容32位CPU
ART將GC暫停由2次改為1次
ART的運行時堆與DVM不同
DVM中的應用每次運行時,字節碼都需要通過JIT編譯器編譯為機器碼,這會使用應用程序的運行效率降低,在ART中,系統在安裝應用時會進行一次AOT(ahead of time compilation,預編譯),將字節碼預先編譯成機器碼并存儲在本地,這樣應用程序運行時就不需要執行編譯了,運行效率會大大提升,設備的耗電也會降低。采用AOT也會有缺點,主要有兩個:(1)AOT會使得應用程序的安裝時間變長,尤其是一些復雜的應用;(2)字節碼預先編譯成機器碼,機器碼需要的存儲空間會多一些。為了解決這兩個問題,在Android7.0中的ART加入了JIT,作為AOT的一個補充,在應用程序安裝時并不會將字節碼全部編譯成機器碼,而是在運行中將熱點代碼編譯成機器碼,從而縮短應用程序安裝時間并節省了存儲空間。
ART的運行時堆
與DVM的GC不同,ART采用了多種垃圾收集方案,每個方案會運行不同的垃圾收集器,默認是采用了CMS(Concurrent Mark-Sweep)方案,該方案主要使用了sticky-CMS和partial-CMS。根據不同的CMS方案,ART的運行時堆的空間也會有不同的劃分,默認是由4個Space和多個輔助數據結構組成的。4個Space分別是Zygote Space、Allocation Space、Image Space、Large Object Space。Zygote Space、Allocation Space和DVM是一樣的。Image Space是用來存放 一些預加載類,Large Object Space用來分配一些大對象。其中 Zygote Space和Image Space是進程間共享的。除了這四個Space,ART的Java堆中還包括兩個Mod Union Table,一個Card Table、兩個Heap Bitmap,兩個Object Map,以及三個Object Stack。
謝謝閱讀。
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。