[原文出處--------------ART運行時垃圾收集機制簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/42072975)
為了學習ART運行時的垃圾收集機制,我們先把Dalvik虛擬機的垃圾收集機制研究了一遍。這是因為兩者都使用到了Mark-Sweep算法,因此它們在概念上有很多一致的地方。然而在實現上,Dalvik虛擬機的垃圾收集機制要簡單一些。這樣我們就可以先從簡單的Dalvik虛擬機垃圾收集機制入手,然后再逐步深入地學習復雜的ART運行時垃圾收集機制。本文就對ART運行時垃圾收集機制進行簡要介紹和制定學習計劃。
與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文件的過程分析](http://blog.csdn.net/luoshengyang/article/details/39307813)一文可以知道,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來描述,它的定義如下所示:
~~~
// 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是一個枚舉類型,它的定義如下所示:
~~~
// 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的定義如下所示:
~~~
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文件的過程分析](http://blog.csdn.net/luoshengyang/article/details/39307813)一文又可以知道,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虛擬機垃圾收集機制簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/41338251)這一系列文章分析Dalvik虛擬機垃圾收集機制之外,還需要繼續從以下情景來進一步分析ART運行時垃圾機制:
1. [ART運行時堆的創建過程](http://blog.csdn.net/luoshengyang/article/details/42379729)。
2. [ART運行時的對象分配過程](http://blog.csdn.net/luoshengyang/article/details/42492621)。
3. [ART運行時的垃圾收集過程](http://blog.csdn.net/luoshengyang/article/details/42555483)。
按照這三個情景學習ART運行時的垃圾收集機制之后,我們就會對上面涉及的概念有一個清晰的認識了。
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析