5分鐘瞭解Android虛擬機Dalvik和ART,讓手機告別卡頓!

一.Dalvik虛擬機

        Dalvik虛擬機(DVM)是Google專門爲Android平臺開發的虛擬機。它運行在Android運行時庫中。其名字來源於作者Bornstein的祖先居住過的名爲Dalvik的小漁村。

1.DVM與JVM的主要區別

        DVM的設計沒有遵循JVM規範來實現,這也是後來導致Google被Oracle起訴的原因。

1)架構不同

        JVM基於棧,DVM基於寄存器。

2)執行字節碼不同

        JVM:.java文件->.class文件->.jar文件
        DVM:.java文件->.class文件->.dex文件->.apk文件
        DVM通過dx工具將所有的class文件整合成一個dex文件,並去掉其中冗餘的信息。

3)DVM的預加載-共享機制

        不同應用運行時可以共享相同的類,JVM不同的程序是獨立的。

2.DVM的運行時堆

        DVM運行時使用標記——清除算法進行GC。由兩個Space和多個輔助數據結構組成。兩個Space爲Zygote Space(Zygote Heap)和Allocation Space(Active Heap)。

1)Zygote Space

        用來管理Zygote進程在啓動過程中預加載和創建的各種對象。Zygote Space不會觸發GC。Zygote進程和應用程序進程間共享Zygote Space。
        在Zygote進程fork第一個子進程之前,會將Zygote Space分成兩部分,原來被使用的部分仍未Zygote Space,而未使用的部分爲Allocation Space。以後的對象都在Allocation Space上進行分配和釋放。Allocation Space在每個進程中都獨立擁有一份,且進程間不共享。

2)Card Table

        用於DVM Concurrent GC,當第一次進行垃圾標記後,記錄垃圾信息。

3)Heap Bitmap

        有兩個Heap Bitmap,一個用來記錄上次GC存活的對象,另一個用來記錄這次GC存活的對象。

4)Mark Stack

        在GC標記階段使用,用來遍歷存活的對象。

3.DVM的GC日誌

1)格式

        D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
參數:
        GC_Reason:引起GC的原因。
        Amount_freed:本次GC釋放內存的大小。
        Heap_stats:堆的空閒內存百分比 (已用內存/堆的總內存)。
        External_memory_stats:API小於等於級別10的內存分配 (已分配的內存/引起GC的閾值)。
        Pause_time:暫停時間。併發暫停時間有兩個:一個在垃圾收集開始,一個在垃圾收集完成。

2)引起GC的原因

        i)GC_CONCURRENT:當堆開始填充時,併發GC可以釋放內存。
        ii)GC_FOR_MALLOC:當堆內存已滿時,App嘗試分配內存而引起的GC,系統必須停止App並回收內存。
        iii)GC_HPROF_DUMP_HEAP:當用戶請求創建HPROF文件來分析堆內存時出現的GC。
        iv)GC_EXPLICIT:顯式GC,例如調用System.gc();。
        v)GC_EXTERNAL_ALLOC:僅適用於API級別小於等於10,且用於外部分配內存的GC。

二.ART虛擬機

        Android4.4後用來替換Dalvik虛擬機。

1.ART和DVM的區別

1)DVM採用JIT編譯,ART採用AOT編譯。

        JIT編譯:Just In Time,DVM中的應用每次運行時,JIT編譯器將字節碼轉換成機器碼。
        AOT編譯:Ahead Of Time,ART中系統在安裝應用程序時會事先將字節碼編譯成機器碼並存儲在本地。

2)DVM爲32位CPU設計,ART支持64位併兼容32位CPU。

3)ART對垃圾回收機制進行了改進。

4)ART的運行時堆劃分與DVM不同。

2.ART的運行時堆

        ART採用多種垃圾收集方案,每個方案運行不同的垃圾收集器。對於不同的方案,ART的運行時堆劃分不同。默認方案爲CMS(併發標記清除)方案,使用sticky-CMS和partial-CMS。默認的ART運行時堆由4個Space和多個輔助數據結構組成。4個Space分別爲Zygote Space、Allocation Space、Image Space、Large Object Space。

1)Zygote Space

        與DVM相同,進程間共享。

2)Allocation Space

        與DVM相同。

3)Image Space

        用來存放一些預加載類,進程間共享。

4)Large Object Space

        用來分配一些大對象,默認大小爲12KB。

5)其它

        ART的Java堆還包含兩個Mod Union Table,一個Card Table,兩個Heap Bitmap,兩個Object Map,以及三個Object Stack。

3.ART的GC日誌

        ART會爲主動請求的垃圾收集事件或者認爲GC速度慢時纔會打印GC日誌。GC速度慢是指GC暫停超過5ms或者GC持續時間超過100ms。

1)格式

        I/art: <GC_Reason> <GC_Name> <Objects_freed> (<Size_freed>) AllocSpace Objects, <Large_objects_freed> (<Large_object_size_freed>) <Heap_stats> LOS objects, <Pause_time(s)>
參數:
        GC_Reason:引起GC的原因。
        GC_Name:垃圾收集器的名稱。
        Objects_freed:本次GC從非Large Object Space中回收的對象的數量。
        Size_freed:本次GC從非Large Object Space中回收的字節數。
        Large_objects_freed:本次GC從Large Object Space中回收的對象數量。
        Large_object_size_freed:本次GC從Large Object Space中回收的字節數。
        Heap_stats:堆的空閒內存百分比,即(已用內存/堆的總內存)。
        Pause_time(s):暫停時間。ART只暫停一次,出現在GC結束。

2)引起GC的原因

        i)Concurrent:併發GC,不會使App的線程暫停,該GC在後臺線程運行,不會阻止內存分配。
        ii)Alloc:當堆內存已滿時,App嘗試分配內存引起的GC,這個GC會發生在正在分配內存的線程中。
        iii)Explicit:App顯式的請求垃圾回收,例如調用System.gc()。
        iv)NativeAlloc:Native內存分配時,觸發的GC。
        v)CollectorTransition:由堆轉換引起的回收,運行時切換GC引起的。將所有對象從空閒列表空間複製到碰撞指針空間,反之亦然。僅出現在內存較小的設備上App將進程從可察覺的暫停狀態更改爲可察覺的非暫停狀態。
        vi)HomogeneousSpaceCompact:齊性空間壓縮是指空閒列表到壓縮的空閒列表空間,通常發生在App移動到可察覺的暫停進程狀態。以此來減小內存使用並對堆內存進行碎片整理。
        vii)DisableMovingGc:不是真正觸發GC的原因。發生併發堆壓縮時,由於使用了GetPrimitiveArrayCritical,收集會被阻塞。
        viii)HeapTrim:不是觸發GC的原因。收集會一直被阻塞,直到堆內存整理完畢。

3)垃圾收集器的名稱:

        i)Concurrent Mark Sweep(CMS):CMS收集器是一種以獲取最短收集暫停時間爲目標的收集器,採用標記-清除算法實現。它是完整的堆垃圾收集器,能釋放除了Image Space外的所有空間。
        ii)Concurrent Partial Mark Sweep:部分完整的堆垃圾收集器,能釋放除了Image Space和Zygote Space外的所有空間。
        iii)Concurrent Sticky Mark Sweep:粘性收集器,基於分代的垃圾收集思想,只能釋放自上次GC以來分配的對象。它比一個完整的或部分完整的垃圾收集器掃描的更頻繁,因爲它更快且暫停時間更短。
        iv)Marksweep + Semispace:非併發的GC,複製GC用於堆轉換以及齊性空間的壓縮。

三.虛擬機的啓動

app_main.cpp中main函數的執行過程:

        判斷若爲Zygote進程,則調用AppRuntime對象的start函數啓動Zygote進程,內部調用startVm函數啓動Java虛擬機。

AndroidRuntime.cpp中start函數的執行過程:

1.調用JniInvocation對象的Init函數,初始化虛擬機環境。
        1)調用GetLibrary函數,獲取系統的虛擬機類型,libart.so或libdvm.so。
        2)調用dlopen函數加載libart.so或libdvm.so。
2.調用startVm函數,啓動虛擬機。
3.調用startReg函數,爲虛擬機註冊JNI方法。

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