與Dalvik虛擬機器垃圾收集機制簡要介紹和學習計劃一文介紹的Dalvik虛擬機器垃圾收集機制一樣,ART執行時垃圾收集機制也涉及到類似於Zygote堆、Active堆、Card Table、Heap Bitmap和Mark Stack等概念,如圖1所示:
圖1 ART執行時垃圾收集機制的基本概念
從圖1可以看到,ART執行時堆劃分為四個空間,分別是Image Space、Zygote Space、Allocation Space和Large Object Space。其中,Image Space、Zygote Space、Allocation Space是在地址上連續的空間,稱為Continuous Space,而Large Object Space是一些離散地址的集合,用來分配一些大物件,稱為Discontinuous Space。
在Image Space和Zygote Space之間,隔著一段用來對映system@framework@boot.art@classes.oat檔案的記憶體。從前面Android執行時ART載入OAT檔案的過程分析一文可以知道,system@framework@boot.art@classes.oat是一個OAT檔案,它是由在系統啟動類路徑中的所有DEX檔案翻譯得到的,而Image Space空間就包含了那些需要預載入的系統類物件。這意味著需要預載入的類物件是在生成system@framework@boot.art@classes.oat這個OAT檔案的時候建立並且儲存在檔案system@framework@boot.art@classes.dex中,以後只要系統啟動類路徑中的DEX檔案不發生變化(即不發生更新升級),那麼以後每次系統啟動只需要將檔案system@framework@boot.art@classes.dex直接對映到記憶體即可,省去了建立各個類物件的時間。之前使用Dalvik虛擬機器作為應用程式執行時時,每次系統啟動時,都需要為那些預載入的類建立類物件。因此,雖然ART執行時第一次啟動時會比較慢,但是以後啟動實際上會更快。
由於system@framework@boot.art@classes.dex檔案儲存的是一些預先建立的物件,並且這些物件之間可能會互相引用,因此我們必須保證system@framework@boot.art@classes.dex檔案每次載入到記憶體的地址都是固定的。這個固定的地址儲存在system@framework@boot.art@classes.dex檔案開頭的一個Image Header中。此外,system@framework@boot.art@classes.dex檔案也依賴於system@framework@boot.art@classes.oat檔案,因此也會將後者固定載入到Image Space的末尾。
Zygote Space和Allocation Space與Dalvik虛擬機器垃圾收集機制中的Zygote堆和Active堆的作用是一樣的。Zygote Space在Zygote程式和應用程式程式之間共享的,而Allocation Space則是每個程式獨佔的。同樣的,Zygote程式一開始只有一個Image Space和一個Zygote Space。在Zygote程式fork第一個子程式之前,就會把Zygote Space一分為二,原來的已經被使用的那部分堆還叫Zygote Space,而未使用的那部分堆就叫Allocation Space。以後的物件都在Allocation Space上分配。
通過上述這種方式,就可以使得Image Space和Zygote Space在Zygote程式和應用程式程式之間進行共享,而Allocation Space就每個程式都獨立地擁有一份。注意,雖然Image Space和Zygote Space都是在Zygote程式和應用程式程式之間進行共享,但是前者的物件只建立一次,而後者的物件需要在系統每次啟動時根據執行情況都重新建立一遍。
為了驗證上面的分析,我們在某一次系統啟動時,檢視Settings.apk程式的地址空間,如圖2所示:
圖2 Settings.apk的地址空間
其中,地址空間0x60000000-0x60a94000對應的就是Image Space,它對映的是system@framework@boot.art@classes.dex檔案,固定對映在地址0x60000000上。緊跟著的地址空間0x60a94000-0x64591000對映的是system@framework@boot.art@classes.oat檔案。再接下來就是Zygote Space和Allocation Space,分別對映在地址空間0x64591000-0x646e7000和0x646e7000-0x6754e000上。並且都是匿名共享記憶體塊。另外,通過翻譯Settings.apk裡面的classes.dex檔案得到的OAT檔案system@priv-app@Settings.apk@classes.dex對映在地址空間0xaf29b000-0xaf654000上。
與Dalivk虛擬機器類似,ART執行時也使用一個Heap物件來描述堆,它的實現如圖3所示:
圖3 ART執行時堆描述類Heap的實現
Heap類包含了以下重要成員變數描述ART執行時的堆,它們的作用如下所述:
1. mark_sweep_collectors_: 一個std::vector<collector::MarkSweep*>向量,儲存了六種Mark-Sweep垃圾收集器。
2. continuous_spaces_: 一個std::vector<space::ContinuousSpace*>向量,儲存了圖1所示的三個在地址空間上連續的Image Space、Zygote Space和Allocation Space。
3. concurrent_gc_: 一個bool變數,描述是否支援並行GC,可以通過ART執行時啟動選項-Xgc來指定。
4. parallel_gc_threads_: 一個size_t變數,指定在GC暫停階段用來同時執行GC任務的執行緒數,可以通過ART執行時啟動選項-XX:ParallelGCThreads指定。如果沒有指定,它的值就等於CPU核心數減1。這裡之所以要減1是因為parallel_gc_threads_描述的實際上是除了當前GC執行緒之外的其它也用於GC任務的執行緒的個數。
5. conc_gc_threads_: 一個size_t變數,指定非GC暫停階段用來同時執行GC任務的執行緒數,可以通過ART執行時啟動選項-XX:ConcGCThreads來指定。
6. discontinuous_spaces_: 一個std::vector<space::DiscontinuousSpace*>向量,儲存了圖1所示的在地址空間上不連續的Large Object Space。
7. alloc_space_: 一個space::DlMallocSpace指標,指向一個space::DlMallocSpace物件,該物件描述的是圖1所示的Allocation Space。
8. large_object_space_: 一個space::LargeObjectSpace指標,指向一個space::LargeObjectSpace物件,該物件描述的是圖1所示的Large Object Space。
9. card_table_: 一個UniquePtr<accounting::CardTable>指標,指向一個accounting::CardTable物件,該物件描述的是圖1所示的Card Table。
10. image_mod_union_table_: 一個UniquePtr<accounting::ModUnionTable>指標,指向一個accounting::ModUnionTable物件,該物件描述的是圖1所示的位於上方的Mod Union Table物件,用來記錄在GC並行階段在Image Space上分配的物件對在Zygote Space和Allocation Space上分配的物件的引用。
11. zygote_mod_union_table_: 一個UniquePtr<accounting::ModUnionTable>指標,指向一個accounting::ModUnionTable物件,該物件描述的是圖1所示的位於下方的Mod Union Table,用來記錄在GC並行階段在Zygote Space上分配的物件對在Allocation Space上分配的物件的引用。
12. mark_stack_: 一個UniquePtr<accounting::ObjectStack>指標,指向一個accounting::ObjectStack物件,該物件描述的是圖1所示的Mark Stack,用來在GC過程中實現遞迴物件標記。
13. allocation_stack_: 一個UniquePtr<accounting::ObjectStack>指標,指向一個accounting::ObjectStack物件,該物件描述的是圖1所示的Allocation Stack,用來記錄上一次GC後分配的物件,用來實現型別為Sticky的Mark Sweep Collector。
14. live_stack_: 一個UniquePtr<accounting::ObjectStack>指標,指向一個accounting::ObjectStack物件,該物件描述的是圖1所示的Live Stack,配合allocation_stack_一起使用,用來實現型別為Sticky的Mark Sweep Collector。
15. mark_bitmap_: 一個UniquePtr<accounting::HeapBitmap>指標,指向一個accounting::HeapBitmap物件,該物件描述的是圖1所示的Mark Bitmap,與Dalvik虛擬機器的Mark Bitmap作用是一樣的,用來標記當前GC之後還存活的物件。
16. live_bitmap_: 一個UniquePtr<accounting::HeapBitmap>指標,指向一個accounting::HeapBitmap物件,該物件描述的是圖1所示的Live Bitmap,與Dalvik虛擬機器的Live Bitmap作用是一樣的,用來標記上一次GC之後還存活的物件。
除了上述的16個成員變數,Heap類還定義了以下三個垃圾收集介面:
1. CollectGarbage: 用來執行顯式GC,例如用實現System.gc介面。
2. ConcurrentGC: 用來執行並行GC,只能被ART執行時內部的GC守護執行緒呼叫。
3. CollectGarbageInternal: ART執行時內部呼叫的GC介面,可以執行各種型別的GC。
上面我們提到,Heap類的成員變數mark_sweep_collectors_儲存了ART執行時內部使用的六種垃圾收集器,這六種垃圾收集器分為兩組。其中一組是支援並行GC的,另一組是不支援並行GC的。每一組都由MarkSweep、PartialMarkSweep和StickyMarkSweep三種型別的垃圾收集器組成。它們的類關係如圖4所示:
圖4 ART執行時的垃圾收集器
StickyMarkSweep繼承於PartialMarkSweep,PartialMarkSweep又繼承於MarkSweep、而MarkSweep又繼承於GarbageCollector。因此,我們可以推斷出,GarbageCollector定義了垃圾收集器介面,而MarkSweep、PartialMarkSweep和StickyMarkSweep通過重定某些介面來實現不同型別的垃圾收集器。
在GarbageCollector中,有一個成員變數heap_,指向的是ART執行時堆,同時定義了兩個public成員函式IsConcurrent和GetGcType。前者用來獲得一個垃圾收集器是否支援並行GC,後者用來獲得垃圾收集器型別 。
垃圾收集器型別通過列舉型別GcType來描述,它的定義如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// The type of collection to be performed. The ordering of the enum matters, it is used to // determine which GCs are run first. enum GcType { // Placeholder for when no GC has been performed. kGcTypeNone, // Sticky mark bits GC that attempts to only free objects allocated since the last GC. kGcTypeSticky, // Partial GC that marks the application heap but not the Zygote. kGcTypePartial, // Full GC that marks and frees in both the application and Zygote heap. kGcTypeFull, // Number of different GC types. kGcTypeMax, }; |
這個列舉型別定義在檔案t/runtime/gc/collector/gc_type.h中。
其中,kGcTypeNone是一個佔位符,kGcTypeMax描述的是垃圾收集器型別個數。kGcTypeSticky指的就是StickyMarkSweep型別的垃圾收集器,用來收集上次GC以來分配的物件。kGcTypePartial指的就是PartialMarkSweep型別的垃圾收集器,用來收集在Allocation Space上分配的物件。kGcTypeFull指的就是MarkSweep型別的垃圾收集器,用來收集在Zygote Space和Allocation Space上分配的物件。
此外,GarbageCollector通過定義以下五個虛擬函式描述GC的各個階段:
1. InitializePhase: 用來實現GC的初始化階段,用來初始化垃圾收集器內部的狀態。
2. MarkingPhase: 用來實現GC的標記階段,該階段有可能是並行的,也有可能不是並行。
3. HandleDirtyObjectsPhase: 用來實現並行GC的Dirty Object標記,也就是遞迴標記那些在並行標記物件階段中被修改的物件。
4. ReclaimPhase: 用來實現GC的回收階段。
5. FinishPhase: 用來實現GC的結束階段。
MarkSweep類通過重寫上述五個虛擬函式實現自己的垃圾收集過程,同時,它又通過定義以下三個虛擬函式來讓子類PartialMarkSweep和StickyMarkSweep實現特定的垃圾收集器:
1. MarkReachableObjects: 用來遞迴標記從根集物件引用的其它物件。
2. BindBitmap: 用來指定垃圾收集範圍。
3. Sweep: 用來回收垃圾物件。
其中,MarkSweep類通過自己實現的成員函式BindBitmap將垃圾收集範圍指定為Zygote和Allocation空間,而PartialMarkSweep和StickyMarkSweep類通過重寫成員函式BindBitmap將垃圾收集範圍指定為Allocation空間和上次GC後所分配的物件。此外,StickyMarkSweep類還通過重定成員函式MarkReachableObjects和Sweep將物件標記和回收限制為上次GC後所分配的物件。
上面我們也提到了ART執行時將堆劃分為連續空間和不連續空間兩種型別,每一種型別又分別有不同的實現。這些實現的類關係如圖5所示:
圖5. ART執行時各種堆空間的關係圖
首先看最下面的三個類ImageSpace、DlMallocSpace和LargeObjectMapSpace。其中,ImageSpace描述的是Image Space,DlMallocSpace描述的是Zygote Space和Allocation Space,LargeObjectMapSpace描述的是Large Object Space。它們都有一個共同的基類Space。
Space類有一個型別為GcRetentionPolicy的成員變數gc_retention_policy_,用來描述一個Space的回收策略。GcRetentionPolicy是一個列舉型別,它的定義如下所示:
1 2 3 4 5 6 7 8 9 10 |
// See Space::GetGcRetentionPolicy. enum GcRetentionPolicy { // Objects are retained forever with this policy for a space. kGcRetentionPolicyNeverCollect, // Every GC cycle will attempt to collect objects in this space. kGcRetentionPolicyAlwaysCollect, // Objects will be considered for collection only in "full" GC cycles, ie faster partial // collections won't scan these areas such as the Zygote. kGcRetentionPolicyFullCollect, }; |
這個列舉型別定義在檔案art/runtime/gc/space/space.h中。
kGcRetentionPolicyNeverCollect表示永遠不會進行垃圾回收的Space,例如Image Space。kGcRetentionPolicyAlwaysCollect表示每一次GC都會嘗試回收垃圾的Space,例如Allocation Space。kGcRetentionPolicyFullCollect表示只有執行型別為kGcTypeFull的GC才會進行垃圾回收的Space,例如Zygote Space。通過Space類的成員函式GetGcRetentionPolicy可以獲得一個Space的回收策略。
一個Space除了具有回收策略之外,還具有Space Type,通過列舉型別SpaceType來描述。列舉型別SpaceType的定義如下所示:
1 2 3 4 5 6 |
enum SpaceType { kSpaceTypeImageSpace, kSpaceTypeAllocSpace, kSpaceTypeZygoteSpace, kSpaceTypeLargeObjectSpace, }; |
這個列舉型別定義在檔案art/runtime/gc/space/space.h中。
kSpaceTypeImageSpace、kSpaceTypeAllocSpace、kSpaceTypeZygoteSpace和kSpaceTypeLargeObjectSpace描述的就分別是Image Space、Allocation Space、Zygote Space和Large Object Space。通過Space類的成員函式GetType可以獲得一個Space的Type。此外,Space類還提供了IsImageSpace、IsDlMallocSpace、IsZygoteSpace和IsLargeObjectSpace四個輔助成員函式來方便判斷一個Space的型別。
前面我們提到,Space劃分Continuous和Discontinuous兩種,其實還可以劃分為Allocable和Non-Allocable兩種。例如,Image Space是不能分配新物件的,而Zygote Space、Allocation Space和Large Object Space是可以分配物件。對於可分配新物件的Space,ART執行時定義了一個基類AllocSpace,它的定義以下幾個重要的介面來描述一個可分配新物件的Space,如下所示:
1. GetBytesAllocated: 獲得當前已經分配的位元組數。
2. GetObjectsAllocated: 獲得當前已經分配的物件數。
3. GetTotalBytesAllocated: 獲得Space自建立以來所分配的位元組數。
4. GetTotalObjectsAllocated: 獲得Space自建立以來所分配的物件數。
5. Alloc: 在Space上分配一個指定大小的物件。
6. AllocationSize: 獲得一個物件佔據的記憶體塊大小。
7. Free: 釋放一個物件佔據的記憶體塊。
8. FreeList: 批量釋放一系列物件佔據的記憶體塊。
Continuous Space和Discontinuous Space分別使用類ContinuousSpace和DiscontinuousSpace來描述,它們都是從類Space繼承下來的。
ContinuousSpace類有兩個成員變數begin_和end_,用來描述一個Continuous Space內部所使用的記憶體塊的開始和結束地址。此外,ContinuousSpace類還定義了以下幾個重要介面來獲得一個Continuous Space的相關資訊,如下所示:
1. Begin: 獲得Space的起始地址。
2. End: 獲得Space的結束地址。
3. Size: 獲得Space的大小。
4. GetLiveBitmap: 獲得Space的Live Bitmap。
5. GetMarkBitmap: 獲得Space的Mark Bitmap。
由於Discontinuous Space在地址空間上是不連續的,因此它不像Continuous Space一樣,可以使用類似begin_和end_的成員變數來確定Space內部使用的記憶體塊。DiscontinuousSpace類在內部使用兩個SpaceSetMap容器live_objects_和mark_objects_來描述已經分配物件集合和在GC過程中被標記的物件集合。此外,DiscontinuousSpace類還定義了以下兩個介面來獲得這兩個物件集合,如下所示:
1. GetLiveObjects: 獲得當前分配的物件集合。
2. GetMarkObjects: 獲得在當前GC過程被標記的物件集合。
Continuous Space內部使用的記憶體塊都是通過記憶體對映得到的,不過這塊記憶體有可能是通過不同方式對映得到的。例如,Image Space內部使用的記憶體塊是通過記憶體對映Image檔案得到的,而Zygote Space和Allocation Space內部使用的記憶體塊是通過記憶體對映匿名共享記憶體得到。通過記憶體對映得到的Space使用類MemMapSpace來描述,它繼承於ContinousSpace。MemMapSpace類有一個成員變數mem_map_,型別是UniquePtr<MemMap>,指向一塊由一個MemMap物件描述的記憶體塊,可以通過成員函式Capacity來獲得該記憶體塊的大小。
從圖5可以看到,Image Space是通過ImageSpace類來描述,它繼承於MemMapSpace類。前面我們提到,Image Space是從一個Image檔案獲得的,就是圖1所示的boot.art@classes.dex檔案,從Android執行時ART載入OAT檔案的過程分析一文又可以知道,Image檔案關聯有一個OAT檔案,就是圖1所示的boot.art@classes.oat檔案。因此,在ImageSpace類內部,有一個成員變數oat_file_,用來描述與Image Space關聯的OAT檔案。此外。在ImageSpace類內部,還有一個型別為UniquePtr<accounting::SpaceBitmap>的成員變數live_bitmap_,用來標記Imape Space的存活物件。由於Image Space是不會進行新物件分配和垃圾回收的,因此它不像其它Space一樣,還有另外一個Mark Bitmap。不過Space要求其子類要有一個Live Bitmap和一個Mark Bitmap,於是,ImageSpace就將內部的live_bitmap_同時作為Live Bitmap和Mark Bitmap來使用。
ImageSpace還通過定義以下幾個介面來獲得Image Space的相關資訊,如下所示:
1. GetType: 重寫了父類Space的成員函式GetType,返回固定為kSpaceTypeImageSpace,表示這是一個Image Space。
2. GetLiveBitmap: 獲得Image Space的Live Bitmap。
3. GetMarkBitmap: 獲得Image Space的Mark Bitmap。
4. Create: 一個靜態成員函式,根據指定的Image檔案建立一個Image Space。
Zygote Space和Allocation Space都是通過類DlMallocSpace來描述。雖然Zygote Space和Allocation Space內部使用的記憶體塊都是通過記憶體對映得到的,不過在使用它們的時候,是通過C庫提供的記憶體管理介面來使用的,因此這裡就將它們的名字命名為DlMalloc,這也是DlMallocSpace的由來。由於DlMallocSpace還是可以分配新物件的,因此在圖5中,我們看到它除了繼承於MemMapSpace類之外,還繼承於AllocSpace。
DlMallocSpace定義了以下幾個重要成員變數來描述內部狀態,如下所示:
1. mspace_: 和Dalvik虛擬機器一樣,ART執行時也是將Zygote Space和Allocation Space使用的匿名共享記憶體塊封裝成一個mspace物件,以便可以使用C庫提供的記憶體管理介面來在上面分配和釋放記憶體。
2. live_bitmap_: 指向一個SpaceBitmap,用來記錄上次GC後存活物件。
3. mark_bitmap_: 指向一個SpaceBitmap,用來記錄當前GC被標記的物件。
4. num_bytes_allocated_: 記錄當前已經分配的記憶體位元組數。
5. num_objects_allocated_: 記錄當前已經分配的物件數。
6. total_bytes_allocated_: 記錄從Space建立以來所分配的記憶體位元組數。
7. total_objects_allocated_: 記錄從Space建立以來所分配的物件數。
DlMallocSpace還新定義或者重寫了父類的成員函式,如下所示:
1. GetType: 重寫了父類Space的成員函式GetType,返回固定為SpaceTypeZygoteSpace或者kSpaceTypeAllocSpace,表示這是一個Zygote Space或者Allocation Space。
2. Create: 一個靜態成員函式,用來建立一個Zygote Space或者Allocation Space。
3. GetBytesAllocated: 重寫了父類AllocSpace的成員函式GetBytesAllocated。
4. GetObjectsAllocated: 重寫了父類AllocSpace的成員函式GetObjectsAllocated。
5. GetTotalBytesAllocated: 重寫了父類AllocSpace的成員函式GetTotalBytesAllocated。
6. GetTotalObjectsAllocated: 重寫了父類AllocSpace的成員函式GetTotalObjectsAllocated。
7. Alloc: 重寫了父類AllocSpace的成員函式Alloc。
8. AllocationSize: 重寫了父類AllocSpace的成員函式AllocationSize。
9. Free: 重寫了父類AllocSpace的成員函式Free。
10. FreeList: 重寫了父類AllocSpace的成員函式FreeList。
11. Capacity: 重寫了父類MemMapSpace的成員函式Capacity。
現在再來看最後一種Space — Large Object Space。ART執行時提供了兩種Large Object Space實現。其中一種實現和Continuous Space的實現類似,預先分配好一塊大的記憶體空間,然後再在上面為物件分配記憶體塊。不過這種方式實現的Large Object Space不像Continuous Space通過C庫的內塊管理介面來分配和釋放記憶體,而是自己維護一個Free List。每次為物件分配記憶體時,都是從這個Free List找到合適的空閒的記憶體塊來分配。釋放記憶體的時候,也是將要釋放的記憶體新增到該Free List去。
另外一種Large Object Space實現是每次為物件分配記憶體時,都單獨為其對映一新的記憶體。也就是說,為每一個物件分配的記憶體塊都是相互獨立的。這種實現方式相比上面介紹的Free List實現方式,也更簡單一些。在Android 4.4中,ART執行時使用的是後一種實現方式,因此我們這裡也主要是介紹這種實現。
為每一物件對映一塊獨立的記憶體塊的Large Object Space實現稱為LargeObjectMapSpace,它與Free List方式的實現都是繼承於類LargeObjectSpace。LargeObjectSpace又分別繼承了DiscontinuousSpace和AllocSpace。因此,我們就可以知道,LargeObjectMapSpace描述的是一個在地址空間上不連續的Large Object Space。
LargeObjectSpace定義了以下重要成員變數描述內部狀態,如下所示:
1. num_bytes_allocated_: 記錄當前已經分配的記憶體位元組數。
2. num_objects_allocated_: 記錄當前已經分配的物件數。
3. total_bytes_allocated_: 記錄從Space建立以來所分配的記憶體位元組數。
4. total_objects_allocated_: 記錄從Space建立以來所分配的物件數。
LargeObjectSpace還重寫了父類AllocSpace的以下成員函式:
1. GetType: 重寫了父類Space的成員函式GetType,返回固定為kSpaceTypeLargeObjectSpace,表示這是一個Large Object Space。
2. GetBytesAllocated: 重寫了父類AllocSpace的成員函式GetBytesAllocated。
3. GetObjectsAllocated: 重寫了父類AllocSpace的成員函式GetObjectsAllocated。
4. GetTotalBytesAllocated: 重寫了父類AllocSpace的成員函式GetTotalBytesAllocated。
5. GetTotalObjectsAllocated: 重寫了父類AllocSpace的成員函式GetTotalObjectsAllocated。
6. FreeList: 重寫了父類AllocSpace的成員函式FreeList。
LargeObjectMapSpace繼承於LargeObjectSpace,它內部有一個成員變數large_objects_,指向一個std::vector<mirror::Object*, accounting::GCAllocator<mirror::Object*> >向量,裡面儲存的就是為每一個物件獨立對映的記憶體塊。
此外,LargeObjectMapSpace還新定義或者重寫了父類AllocSpace的以下成員變數:
1. Create: 一個靜態成員函式,用來建立一個LargeObjectMapSpace。
2. Alloc: 重寫了父類AllocSpace的成員函式Alloc。
3. AllocationSize: 重寫了父類AllocSpace的成員函式AllocationSize。
4. Free: 重寫了父類AllocSpace的成員函式Free。
除了Garbage Collector和Space,ART執行時垃圾收集機制比Dalvik垃圾收集機制還多了一個稱Mod Union Table的概念。Mod Union Table是與Card Table配合使用的,用來記錄在一次GC過程中,記錄不會被回收的Space的物件對會被回收的Space的引用。例如,Image Space的物件對Zygote Space和Allocation Space的物件的引用,以及Zygote Space的物件對Allocation Space的物件的引用。
ART執行時定義了一個ModUnionTable基類以及一系列的子類,用來記錄不同Space的物件對特定Space的物件的引用,它們的關係如圖6所示:
圖6 ART執行時的Mod Union Table實現
ModUnionTable類有一個成員變數heap_,指向ART執行時堆,可以通過成員函式GetHeap來獲得。
在GC標記階段,Card Table和Mod Union Table分三步處理。第一步是呼叫ModUnionTable類的成員函式ClearCards清理Card Table裡面的Dirty Card,並且將這些Dirty Card記錄在Mod Union Table中。第二步是呼叫ModUnionTable類的成員函式Update將遍歷記錄在Mod Union Table裡面的Drity Card,並且找到對應的被修改物件,然後將被修改物件引用的其它物件記錄起來。第三步是呼叫ModUnionTable類的成員函式MarkReferences標記前面第二步那些被被修改物件引用的其它物件。通過這種方式,就可以使用Card Table可以在標記階段重複使用,即在執行第二步之前,重複執行第一步,最後通過Mod Union Table將所有被被修改物件引用的其它物件收集起來統一進行標記,避免對相同物件進行重複標記。
上面說到,Mod Union Table的作用就使得Card Table可以重複使用,前提是Mod Union Table將每一次清理Card Table之前,將之前已經是Dirty的Card快取起來。因此,ModUnionTableCardCache類繼承ModUnionTable類的時候,定義了一個型別為CardSet的成員變數cleared_cards_,就是用來快取Dirty Card的,並且它重寫了父類ModUnionTable的成員函式ClearCards、Update和MarkReferences來實現自己的DIrty Card快取策略。
另外一個繼承於ModUnionTable的子類ModUnionTableReferenceCache,不單會將Dirty Card快取起來儲存在型別同樣為CardSet的成員變數cleared_cards_中,而且還將與這些Dirty Card對應的被引用物件也快取起來儲存在成員變數references_指向的一個SafeMap中。同樣的,ModUnionTableReferenceCache重寫了父類ModUnionTable的成員函式ClearCards、Update和MarkReferences來實現自己的Dirty Card快取策略。此外,ModUnionTableReferenceCache還提供了一個AddReference介面,用來決定哪些被引用物件需要快取。
ModUnionTableReferenceCache類並不是直接使用的,它也是作為基類使用。ART執行時提供了兩個子類ModUnionTableToZygoteAllocspace和ModUnionTableToAllocspce,它們都繼承於ModUnionTableReferenceCache類。從名字我們可以大致推斷出,ModUnionTableToZygoteAllocspace類是用來記錄位於Zygote Space和Allocation Space的被引用物件的,而ModUnionTableToAllocspace是用來記錄位於Allocation Space的被引用物件的,也就是對應於前面圖1所示的兩個Mod Union Table。不過ART執行時實際上是使用ModUnionTableCardCache來記錄位於Allocation Space的被引用物件的。在後面的文章中,我們就可以看到這一點。
至此,ART執行時垃圾收集機制涉及到的基礎概念我們就介紹完畢。要做到完全理解這些概念,除了要熟悉Dalvik虛擬機器垃圾收集機制簡要介紹和學習計劃這一系列文章分析Dalvik虛擬機器垃圾收集機制之外,還需要繼續從以下情景來進一步分析ART執行時垃圾機制:
1. ART執行時堆的建立過程。
2. ART執行時的物件分配過程。
3. ART執行時的垃圾收集過程。
按照這三個情景學習ART執行時的垃圾收集機制之後,我們就會對上面涉及的概念有一個清晰的認識了。敬請關注!想了解更多資訊,也可以關注老羅的新浪微博:http://weibo.com/shengyangluo。