表14-1 MarkSweep类家族GetGcType取值情况
图14-7 CMS时Heap位图相关成员变量取值情况
一个HeapBitmap对象可以管理多个ContinuousSpace的某一种位图对象
MarkSweep::MarkSweep(Heap* heap, bool is_concurrent,
const std::string& name_prefix)
: GarbageCollector(heap, name_prefix +
(is_concurrent ? "concurrent mark sweep": "mark sweep")),
current_space_bitmap_(nullptr),
mark_bitmap_(nullptr), mark_stack_(nullptr),
.....
is_concurrent_(is_concurrent),...... {
/*MarkSweep构造函数并不复杂,此处先介绍下它的几个成员变量(其作用留待后续代码分析时时再详细讲解):
current_space_bitmap_:类型为ContinuousSpaceBitmap*。
mark_bitmap_:类型为HeapBitmap*。
mark_stack_:类型为ObjectStack*。 */
.....
/*下面的代码行将创建一个内存映射对象。ART内部大量使用内存映射对象。下面的
sweep_array_free_buffer_mem_map_的用法需要到介绍StickyMarkSweep时才能见到。总之,读者将它看作一块内存即可。*/
MemMap* mem_map = MemMap::MapAnonymous(......);
sweep_array_free_buffer_mem_map_.reset(mem_map);
.....
}
graph TB
IsConcurrent{IsConcurrent}-->|yes|MarkingPhaseC(MarkingPhase)
MarkingPhaseC-->pauseC(pause)
pauseC-->PausePhaseC("PausePhase_reMark")
PausePhaseC-->RevokeAllThreadLocalBuffers
RevokeAllThreadLocalBuffers-->ReclaimPhase
ReclaimPhase-->FinishPhase
IsConcurrent-->|no|pause
pause-->MarkingPhase
MarkingPhase-->PausePhase
PausePhase-->RevokeAllThreadLocalBuffers
void MarkSweep::RunPhases() {
Thread* self = Thread::Current();
//初始化MarkSweep类的几个成员变量。其中,MarkSweep的mark_bitmap_将设置为Heap的成员变量mark_bitmap_(读者可回顾图14-7)
InitializePhase();
if (IsConcurrent()) {//if条件为true,则是CMS的行为
......
{ /*CMS和MS的区别:下面代码中ReaderMutexLock为辅助类,真正用于同步的关键对象为
mutator_lock_。它是一个全局定义的读写互斥锁。即支持多个线程同时进行读操作。但如果
某个线程要执行写操作的话,必须等待所有执行读操作的线程释放这个锁。同理,执行写操作的
线程如果先抢到这个锁的话,其他想做读操作或写操作的线程都必须要等待当前拥有这个锁的写
线程释放该锁。ReaderMutextLock在构造函数中将针对mutator_lock_申请一个读锁,而在析构函数中释放读锁。*/
ReaderMutexLock mu(self, *Locks::mutator_lock_);
MarkingPhase();//①标记工作,详情见下文代码分析,main
}
/*ScopedPause也是一个辅助类,其构造函数中会暂停除调用线程外其他所有Java线程的运行,其内部
调用ThreadList的SuspendAll,详情可参考12.2.3节的内容。ScopedPause的析构函数中会恢复这些线程的运行。
简单来说,下面的这段代码运行时,其他Java线程将停止运行。 */
ScopedPause pause(this);
....
PausePhase();//②PausePhase的详情见下文代码分析,main
/*撤销线程对象的TLAB空间。此后Thread TLAB空间为0,TLAB的作用在于加速内存分配的速度。
TLAB所需的内存资源来自对应的空间对象,例如BumpPointerSpace、RegionSpace等。请读者
注意,Revoke是撤销的意思,不是Free(释放)。撤销TLAB之后那些创建在TLAB上的对象依然存在。
这些对象中的垃圾对象将在后续清除阶段回收。*/
RevokeAllThreadLocalBuffers();
} else {
//如果回收器类型为MS,则先暂停其他Java线程的运行,
ScopedPause pause(this);
.....
MarkingPhase();
......
PausePhase();
RevokeAllThreadLocalBuffers();
}
//标记相关的工作结束,开始准备清除工作
{ //注意,mutator_lock_被用作读锁。这和上面CMS逻辑中调用MarkingPhase函数的处理
//一样。这说明无论CMS还是MS,清除任务(Reclaim Phase)可以和mutator同时执行
ReaderMutexLock mu(self, *Locks::mutator_lock_);
ReclaimPhase();//③回收工作,详情见下文代码分析,main
}
.....
FinishPhase();//④GC的收尾,详情见下文代码分析,main
}
……
graph LR
MarkingPhase-->|标记根对象|MarkRoots-->VisitRoots("Runtime::Current()->VisitRoots(this)")
MarkRoots-->PushOnMarkStack
MarkingPhase-->|从根对象触发编辑所有能追踪到的对象|MarkReachableObjects-->Object.VisitReferences
void MarkSweep::MarkingPhase() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
Thread* self = Thread::Current();
//①我们单独介绍下面三个函数调用,笔者将它们归为标记前的准备工作
BindBitmaps();
FindDefaultSpaceBitmap();
/*调用Heap ProcessCards函数,该函数的作用见下文解释。此处请注意最后一个参数的取值:
(1) MarkSweep GetGcType返回kGcTypeFull,所以最后一个参数取值为false。
(2) PartialMarkSweep GetGcType返回kGcTypePartial,所以最后一个参数取值为false。
(3) StickyMarkSweep GetGcType返回值为kGcTypeSticky,所以最后一个参数取值为
true。 */
heap_->ProcessCards(GetTimings(), false, true, GetGcType() != kGcTypeSticky);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
//②标记相关函数,,标记的信息保存在各个空间的mark_bitmap_中
MarkRoots(self);//MarkRoots用于标记根对象
MarkReachableObjects();//从根对象出发以标记所有能追踪到的对象
//③下面这个函数和CMS有关,我们后续统一介绍它
PreCleanCards();
}
void MarkSweep::BindBitmaps() {
......
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
/*搜索Heap continuous_spaces_数组中的空间对象,如果某个空间对象的gc_retention_policy_
成员变量取值为kGcRetentionPolicyNeverCollect,则将它加入MarkSweep immune_spaces_
(类型为ImmuneSpace,其内部有一个std set容器)中。回顾13.7.1节的内容可知,只有ImageSpace
(结合14.3.3节的图14-6可知,它就是boot.art文件映射到内存后得到的空间对象)满足这个条
件。再次请读者注意,kGcRetentionPolicyNeverCollect表示不用对该空间的对象进行垃圾回收。*/
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect) {
immune_spaces_.AddSpace(space);//AddSpace的代码见下文介绍
}
}
}
immune_spaces.cc
void ImmuneSpaces::AddSpace(space::ContinuousSpace* space) {
//ImageSpace重载了GetLiveBitmap和GetMarkBitmap函数,返回的都是
//ImageSpace的live_bitmap_成员。所以,下面的if条件对ImageSpace而言并不满足
if (space->GetLiveBitmap() != space->GetMarkBitmap()) {
//调用ContinuousMemMapAllocSpace BindLiveToMarkBitmap函数。它的作用我们
//在StickyMarkSweep类中再来介绍。MarkSweep还用不到它
space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
}
//spaces_是ImmuneSpace的成员,为一个std set集合
spaces_.insert(space);
//ImmuneSpaces是一个辅助类的数据结构
CreateLargestImmuneRegion();
}
void MarkSweep::FindDefaultSpaceBitmap() {
......
//遍历Heap continuous_space_数组,读者可回顾图14-6了解CMS情况下Heap continuous_space_数组的取值情况
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
accounting::ContinuousSpaceBitmap* bitmap = space->GetMarkBitmap();
/*参考13.7.1节的内容可知,DlMallocSpace和RosAllocSpace
都满足下面的if条件(它们的回收策略为kGcRetentionPolicyAlwaysCollect)。
if条件满足后,将把空间对象中的mark_bitmap_赋值给MarkSweep的成员变量 current_space_bitmap_。*/
if (bitmap != nullptr && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) {
//current_space_bitmap_为MarkSweep的成员变量,指向一个ContinuousSpaceBitmap对象
current_space_bitmap_ = bitmap;
//从下面这个if条件来看,current_space_bitmap_取值来自Heap main_space_的
//mark_bitmap_。读者可回顾14.3.3节中的图14-6
if (space != heap_->GetNonMovingSpace()) { break; }
}
}
}
void Heap::ProcessCards(..., bool use_rem_sets, bool process_alloc_space_cards, bool clear_alloc_space_cards) {
/*注意参数,MarkSweep 调用它时,所传入的参数值为:
use_rem_sets为false。 process_alloc_space_cards为true。clear_alloc_space_cards为true。 */
//遍历continuous_spaces_数组
for (const auto& space : continuous_spaces_) {
//找到和这个space关联的ModUnionTable或者RememberedSet对象
//无论使用ModUnionTable还是RememberedSet,最终操作的都是Heap card_table_
accounting::ModUnionTable* table = FindModUnionTableFromSpace(space);
accounting::RememberedSet* rem_set = FindRememberedSetFromSpace(space);
/*在Heap PreZygoteFork被调用前,Heap中space对象的情况见图14-6。在那里,
ImageSpace对象关联了一个ModUnionTable对象,其余两个空间对象各关联了
一个RememberedSet对象。 */
if (table != nullptr) {
/*调用ModUnionTable的ClearCards函数,其作用我们在13.8.2.4节中曾介绍过。
该函数调用的结果是:
这个ModUnionTable管理的空间对象在Heap CardTable中对应card的值将发生如下变化:
(1) 如果card旧值为kCardDirty,则设置其新值为kCardDirty - 1,
(2) 否则设置其新值为0。*/
table->ClearCards();
} else if (use_rem_sets&& rem_set != nullptr) {
/*在本例中,use_rem_sets取值为false,if条件不满足。RememberedSet ClearCards
函数的代码见13.8.2.3.1节。其效果和上面ModUnionTableClearCards一样。 */
rem_set->ClearCards();
} else if (process_alloc_space_cards) {//对本例而言,该参数为true
if (clear_alloc_space_cards) {//满足条件
/*下面将处理card_table_中覆盖space对象的card信息。ClearCardRange函数
将设置对应card的值为0。*/
uint8_t* end = space->End();
......
card_table_->ClearCardRange(space->Begin(), end);
} else {
/*如果clear_alloc_space_cards为false,则调用CardTable的
ModifyCardsAtomic函数修改对应内存范围的card的值。其中:
(1) 如果card的旧值为kCardDirty,则新值为kCardDirty-1
(2) card的旧值为非kCardDirty时,新值为0。*/
card_table_->ModifyCardsAtomic(space->Begin(), space->End(),
AgeCardVisitor(),VoidFunctor());
}
} } }
void MarkSweep::MarkRoots(Thread* self) {
......
if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
/*如果if条件为true,说明其他Java线程已暂停运行。14.2节中我们已经介绍过Runtime
VisitRoots函数了。此处,MarkSweep实现了RootVisitor接口。下面将直接介绍MarkSweep
所实现的RootVisitor VisitRoots接口函数。*/
Runtime::Current()->VisitRoots(this);
/*下面这个函数将遍历所有Thread对象,设置它们的tlsPtr_thread_local_alloc_stack_
end和thread_local_alloc_stack_top为空,即收回线程的Allocation Stack空间。
注意,结合图14-8的内容和13.6.4.3节的相关知识可知,此处只是将线程的Allocation Stack
空间大小设置为0,而存储在Allocation Stack中的信息依然存在(因为Heap allocation_
stack_没有被修改)。*/
RevokeAllThreadLocalAllocationStacks(self);
} else {
......//和CMS有关,详见14.4.9节
}
}
void MarkSweep::VisitRoots(mirror::Object*** roots, size_t count,const RootInfo& info) {
for (size_t i = 0; i < count; ++i) {
MarkObjectNonNull(*roots[i]);
}
}
void MarkSweep::VisitRoots(CompressedReference<Object>** roots, size_t count,const RootInfo& info) {
for (size_t i = 0; i < count; ++i) {
MarkObjectNonNull(roots[i]->AsMirrorPtr());
}
}
//对Obj进行标记。标记就是设置该Obj所在空间对象的mark_bimap_位图对应位的值为1。
//这个新被标记的Obj保存到MarkSweep mark_sweep_容器中。
inline void MarkSweep::MarkObjectNonNull(mirror::Object* obj,
mirror::Object* holder, MemberOffset offset) {
//MarkObjectNonNull最后两个参数有默认值,分别为nullptr和MemberOffset(0)
......
//如果obj位于immune_spaces_所包含的空间对象中,则无须标记,详情见if中的解释
if (immune_spaces_.IsInImmuneRegion(obj)) {
......
/*注意下面这句调试时才会执行的代码。MarkSweep成员变量mark_bitmap_类型为HeapBitmap,
它其实就是Heap的mark_bitmap_成员。回顾本章的图14-6可知,HeapBitmap包含了所有
连续空间对象的mark_bitmap_成员。不过,对ImageSpace来说,它的live_bitmap_也被
包含在Heap mark_bitmap_中了。
在下面这行代码中,Test函数将测试obj在位图中对应的位是否为1。如果Test返回值为0,
DCHECK会打印一条错误信息(如果打开调试的话)。这说明一个Obj如果位于ImageSpace空
间的话,它一定是存活的(同时也是早就被标记了的。因为ImageSpaceGetMarkBitmap返回
的也是live_bitmap_)。但是,请读者注意,尽管位于ImageSpace空间中的对象是长久存
活的,但是这些对象的引用型成员变量所指向的对象却可能位于其他空间,而这些对象就可能
是垃圾。*/
DCHECK(mark_bitmap_->Test(obj));
} else if (LIKELY(current_space_bitmap_->HasAddress(obj))) {
/*根据“准备工作”一节FindDefaultSpaceBitmap函数可知,current_space_bitmap_
为某个空间对象的mark_bitmap_。判断一个Obj是否被标记的标准就是该Obj
在mark_bitmap_中对应位的值是否为1。所以,本段代码的主要工作可总结为:
(1) HasAddress检查current_space_bitmap_对应的内存空间是否包含了obj对象
(2) 如果满足条件,则调用Set函数设置obj对应位的值为1。于是,这个Obj就算被标记了。
(3) Set函数返回该位的旧值。如果旧值为0,说明这个obj之前没有被标记。调用
PushOnMarkStack将obj加入到MarkSweep mark_stack_容器中。mark_stack_为 一个ObjectStack对象。 */
if (UNLIKELY(!current_space_bitmap_->Set(obj))) {
PushOnMarkStack(obj);
}
} else {
/*说明obj不在current_space_bitmap_所关联的那个空间对象中,此时就需要搜索Heap
所有的空间对象,显然这比直接操作current_space_bitmap_要耗时。从这里可以看出,
使用current_space_bitmap_是一种优化处理。后面我们还会看到类似的这种优化处理。*/
......
/*mark_bitmap_指向一个HeapBitmap对象,它就是Heap中的mark_bitmap_。根据图
14-8的介绍,HeapBitmap管理了所有空间对象的SpaceBitmap。下面的Set函数将搜索
所有空间对象,找到包含这个Obj的Space对象,然后设置对应位的值。 */
if (!mark_bitmap_->Set(obj,...)) {
PushOnMarkStack(obj);
}
}
}
void MarkSweep::MarkReachableObjects() {
//处理immune_spaces_空间中的对象,对理解card table的作用非常关键,请读者注意
UpdateAndMarkModUnion();
RecursiveMark();//我们重点介绍这个函数
}
void MarkSweep::UpdateAndMarkModUnion() {
for (const auto&space : immune_spaces_.GetSpaces()) {
.......
/*space要么是ImageSpace,要么是ZygoteSpace。如果它们关联了一个ModUnionTable
对象,则通过ModUnionTable的UpdateAndMarkReference函数来处理。否则通过它们的
live_bitmap_来处理。其中:
UpdateAndMarkReference的参数是一个MarkObjectVisitor类型的函数调用对象,MarkSweep
类实现了它的MarkObject和MarkHeapReference接口函数。
而live_bimap_ VisitMarkedRange函数最后一个参数为函数调用对象。最终,不管下面
代码中的if和else哪个条件满足,MarkSweep的MarkObjectNonNull都将被调用。 */
accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space);
if (mod_union_table != nullptr) {
mod_union_table->UpdateAndMarkReferences(this);
} else {
//如果该空间没有关联ModUnionTable,则只能遍历该空间的所有存活对象了
space->GetLiveBitmap()->VisitMarkedRange(
reinterpret_cast<uintptr_t>(space->Begin()),
reinterpret_cast<uintptr_t>(space->End()),
ScanObjectVisitor(this));
}
}
}
void MarkSweep::RecursiveMark() {
......
if (kUseRecursiveMark) {//kUseRecursiveMark为编译常量,默认值为false
....//这部分代码中有和parallel处理有关的内容,感兴趣的读者可自行阅读
}
ProcessMarkStack(false);//此处传递的参数为false
}
void MarkSweep::ProcessMarkStack(bool paused) {
/*ProcessMarkStack就是遍历mark_stack_中的obj对象,追踪它们的引用型参数。
注意,追踪根对象的引用型成员变量是一个非常耗时的工作,所以可以利用多线程来处理,这就是
parallel collection的一个体现。根据下面的if条件判断,是否使用parallel gc需要
满足一定条件,即:
(1) kParallelProcessMarkStack:编译常量,默认取值为true.
(2) GetThreadCount返回值大于1。
(3) mark_stack_所保存的Obj对象个数大于128(kMinimumParallelMarkStackSize的值)
GetThreadCount的代码请读者自行阅读。其中会涉及kProcessStateJankPerceptible枚举
变量。我们在10.3.2.2节中曾介绍过它。当应用处于前台时,它的进程状态会被设置为这个值,表
示如果应用发生卡顿,用户是能感受到的。*/
size_t thread_count = GetThreadCount(paused);//
if (kParallelProcessMarkStack && thread_count > 1 &&
mark_stack_->Size() >= kMinimumParallelMarkStackSize) {
ProcessMarkStackParallel(thread_count);//后文再详细介绍这个函数
} else {
/*else代码块为不使用parallel collection的处理。处理方式很简单,就是遍历
mark_stack_中的元素,调用它们的VisitReference函数。每找到一个引用型参数就调用
MarkSweep MarkObject函数进行标记。如果是新标记的对象,就将其加入mark_stack_
容器中。如此往复直到mark_stack_中的元素都处理完为止。 */
static const size_t kFifoSize = 4;
BoundedFifoPowerOfTwo<mirror::Object*, kFifoSize> prefetch_fifo;
for (;;) {
mirror::Object* obj = nullptr;
if (kUseMarkStackPrefetch) {//kUseMarkStackPrefetch默认为true
/*这段代码为一种优化实现,利用了GCC的__builtin_prefetch功能加速获取
mark_stack_中的元素。感兴趣的读者可以自行研究它。*/
.....
} else {
//如果不使用优化实现的话,遍历mark_stack_的代码逻辑就非常简单了
if (mark_stack_->IsEmpty()) { break; }
obj = mark_stack_->PopBack();
}
/*ScanObject将调用Object VisitReferences,所设置的函数调用对象最终会通过MarkSweep
MarkObject函数来标记所找到的引用型成员变量。我们在13.8.3节中曾介绍过VisitReferences
函数。注意,对Reference类型的对象有一些特殊处理。我们后文将介绍这部分内容。 */
ScanObject(obj);
}
}
}
void MarkSweep::PausePhase() {
Thread* self = Thread::Current();
if (IsConcurrent()) {//①如果是CMS,则需要调用下面两个函数
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
/*回顾MarkSweep RunPhases可知,在CMS情况下gc线程执行MarkingPhase的时候,mutator
线程可同时运行。也就是说,对CMS而言,MarkingPhase标记的对象可能还不全面,所以在
PausePhase的时候要重新做一次标记。当然,这次标记不会像MarkingPhase那样耗时,否则
CMS就没有什么价值了。 */
ReMarkRoots();
RecursiveMarkDirtyObjects(true, accounting::CardTable::kCardDirty);
}
{
......
//写锁,使用全局静态变量heap_bitmap_lock_同步对象
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
/*②调用Heap SwapStack函数,内部将执行下面这条语句:
allocation_stack_.swap(live_stack_)。它将live_stack_和allocation_stack_
的内容进行交换。 */
heap_->SwapStacks();
live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
/*再次清空Thread的Allocation Stack空间。注意,我们在MarkRoots函数中也
调用过这个函数。两次执行它的原因是因为在MarkRoots中清空之后直到代码运行到这里时,
可能有mutator线程又重新分配和使用了Allocation Stack。*/
RevokeAllThreadLocalAllocationStacks(self);
}
//③下文将简单介绍Runtime DisallowNewSystemWeaks的内容
Runtime::Current()->DisallowNewSystemWeaks();
//④下面这行代码和Java Reference对象的处理有关,我们后续单独介绍这部分知识
GetHeap()->GetReferenceProcessor()->EnableSlowPath();
}
void MarkSweep::RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age) {
/*ScanGrayObjects是一个比较复杂的函数,但理解它并不难,笔者仅介绍其功能,感兴趣的
读者可以自行研究它的代码。
在StickyMarkSweep MarkReachableObjects中,mark_stack_被清空。这并不是说
StickyMarkSweep不需要它,而是StickyMarkSweep需要往mark_stack_填充自己的内
容(MarkRoots往mark_stack_填充的对象算MarkSweep的)—该工作由下面的
ScanGrayObjects完成。ScanGrayObjects的功能很简单:
(1) 遍历Heap continuous_spaces_中的空间对象。每找到一个mark_bitmap_不为空指针
的空间对象,就转到2去执行。
(2) 调用Heap card_table_的Scan函数。找到那些card值大于或等于minimum_age的card,
然后根据这个card再到空间对象去找到对应的mirror Object对象。注意,这些对象必须
是在空间对象mark_bitmap_所标记过了的。
(3) 每找到这样的一个mirror Object对象就调用MarkSweep的ScanObject以标记它的
引用型成员变量。ScanObject内部会调用MarkSweep MarkObject进行标记处理。
现在我们以某个空间对象A为例来说明和ScanGrayObjects有关的处理逻辑:
(1) BindBitmaps中,A的mark_bitmap_的内容替换成了live_bitmap_。这表示
mark_bitmap_保存了上次GC后留存的对象。
(2) A对应的card在Heap ProcessCards中被修改为kCardDirty – 1或者0。
值为kCardDirty – 1的card表示对应的对象的引用型成员变量被修改过。
(3) ScanGrayObject扫描属于A的并且值为kCardDirty -1的card。然后找到这些card
中被标记了的对象(对象是否被标记由于A的mark_bitmap_决定)。
(4) 每找到一个这样的对象就调用MarkObject对它们的引用型成员变量进行标记。
简单来说,ScanGrayObjects就是确定被标记过的对象中有哪些对象的引用型成员变量被
修改过。main*/
ScanGrayObjects(paused, minimum_age);
//下面这个函数在14.4.3.2.2节中介绍过该函数
ProcessMarkStack(paused);
}
runtime.cc
void Runtime::DisallowNewSystemWeaks() {
//DisallocwNewSystemWeaks涉及ART虚拟机的很多个模块,比如下面的monitor_list_、
//intern_table_、java_vm_等。出于篇幅考虑,本节仅介绍java_vm_的情况
monitor_list_->DisallowNewMonitors();
intern_table_->ChangeWeakRootState(gc::kWeakRootStateNoReadsOrWrites);
//禁止JNI层创建新的WeakGlobal对象,或者解析一个WeakGlobal对象。我们简单介绍它对创建WeakGlobal型对象的影响
java_vm_->DisallowNewWeakGlobals();
heap_->DisallowNewAllocationRecords();
lambda_box_table_->DisallowNewWeakBoxedLambdas();
}
void JavaVMExt::DisallowNewWeakGlobals() {
Thread* const self = Thread::Current();
MutexLock mu(self, weak_globals_lock_);
//下面这个变量的数据类型为Atomic<bool>,设置其值为false
allow_accessing_weak_globals_.StoreSequentiallyConsistent(false);
}
graph LR
SweepWalk-->|在live_bitmap中但不在mark_bitmap中,认为是垃圾对象,回调|SweepCallback-->从live_bitmap中清除
SweepCallback-->space.free释放空间
void MarkSweep::ReclaimPhase() {
Thread* const self = Thread::Current();
ProcessReferences(self);//①对Java Reference对象的处理,我们后续统一介绍
//②清除系统中"Weak"型的垃圾对象。我们将介绍JNI WeakGlobal型对象的清除
SweepSystemWeaks(self);
Runtime* const runtime = Runtime::Current();
runtime->AllowNewSystemWeaks();//重新允许"Weak"对象的创建和解析
//清除不再需要的ClassLoader对象。请感兴趣的读者自行研究
runtime->GetClassLinker()->CleanupClassLoaders();
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
GetHeap()->RecordFreeRevoke();
//③下面的Sweep函数是关键,用于清理之前未被标记的对象
Sweep(false);//注意,此处调用Sweep的参数为false
//下面这两个函数用于处理空间对象中的位图
SwapBitmaps();
//UnBindBitmaps的处理需结合StickyMarkSweep来介绍
GetHeap()->UnBindBitmaps();
}
}
void MarkSweep::SweepSystemWeaks(Thread* self) {
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
/*调用Runtime SweepSystemWeaks函数,参数为一个IsMarkedVisitor类型的对象。
根据14.3.1节的介绍可知,IsMarkedVisitor是一个虚基类,仅定义
了一个IsMarked虚函数。GarbageCollector类继承了IsMarkedVistior类。而
IsMarked由GarbageCollector的具体子类来实现。*/
Runtime::Current()->SweepSystemWeaks(this);
}
inline mirror::Object* MarkSweep::IsMarked(mirror::Object* object) {
//先看看这个object是否属于immue_spaces_空间中的对象
if (immune_spaces_.IsInImmuneRegion(object)) {
return object;
}
//current_space_bitmap_来自某个Space对象的mark_bitmap_,先检查这个object是否
//属于该空间,然后判断它是否被标记
if (current_space_bitmap_->HasAddress(object)) {
return current_space_bitmap_->Test(object) ? object : nullptr;
}
//mark_bitmap_就是Heap mark_bitmap_的成员,它将遍历Heap的所有space对象,
//先判断object属于哪个空间,然后检查是否被标记。
return mark_bitmap_->Test(object) ? object : nullptr;
}
void Runtime::SweepSystemWeaks(IsMarkedVisitor* visitor) {
GetInternTable()->SweepInternTableWeaks(visitor);
GetMonitorList()->SweepMonitorList(visitor);
//笔者仅介绍JNI层对WeakGlobal型对象的清除过程
GetJavaVM()->SweepJniWeakGlobals(visitor);
GetHeap()->SweepAllocationRecords(visitor);
GetLambdaBoxTable()->SweepWeakBoxedLambdas(visitor);
}
java_vm_ext.cc
void JavaVMExt::SweepJniWeakGlobals(IsMarkedVisitor* visitor) {
MutexLock mu(Thread::Current(), weak_globals_lock_);
Runtime* const runtime = Runtime::Current();
/*weak_globals_的类型为IndirectReferenceTable(笔者简写其为IRTable),
在下面这段C++11的for each循环中,entry的类型为GcRoot<mirror::Object>*。
它直接来自IRTable的成员变量table_。如果修改了entry的值,也就是修改了
weak_globals_的内容。*/
for (auto* entry : weak_globals_) {
//遍历weak_globals_中的元素,元素是一个WeakGlobal型的对象
if (!entry->IsNull()) {
//调用GcRoot的Read函数,不使用ReadBarrier。GcRoot的Read函数其实很有讲究,
//主要和Read Barrier的处理有关。本书所使用的例子均不使用Read barrier
mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
/*调用IsMarkedVisitor的IsMarked函数。对MS而言,此处调用的是MarkSweep类
的IsMarked函数,下文将看到它的代码。IsMarked返回值为一个Object对象。如果
MarkSweep IsMarked返回为空指针,说明输入obj没有被标记—说明该obj是
垃圾对象。 */
mirror::Object* new_obj = visitor->IsMarked(obj);
if (new_obj == nullptr) {
/*new_obj为空指针,说明这个WeakGlobal型对象指向的那个对象是垃圾,将会被清除。
这时我们需要修改WeakGlobal型对象的内容,使它指向另外一个有特殊含义的对象—
即Runtime的sentinel_成员变量(由GetClearedJniWeakGlobal函数返回)。Runtime
sentinel_就是一个Java Object对象,它在ClassLinker InitFromBootImage
函数中创建。该对象本身没有什么特别之处,只不过它有特殊用途而已。*/
new_obj = runtime->GetClearedJniWeakGlobal();
}
//修改WeakGloabl型对象的内容
*entry = GcRoot<mirror::Object>(new_obj);
}
}
}
void MarkSweep::Sweep(bool swap_bitmaps) {//注意,调用时swap_bitmaps为false
......
{
/*GetLiveStack返回Heap的live_stack_。14.4.4节中介绍过Heap live_stack_的内容。它
保存了mutator线程所创建的对象。从严格意义上来说是从下面的Reset调用后到PausePhase调用
Heap SwapStacks之前这段时间内mutator创建的对象。为什么这么说呢?原因是在于它们的使用
步骤:
(1) mutator只会将创建的对象存储于Heap allocation_stack_中。
(2) Heap SwapStacks将交换Heap live_stack_和allocation_stack_的内容。此后,
allocation_stack_容器为空容器(原因在步骤3)。
(3) Heap MarkAllocStackAsLive后,live_stack_会被Reset,也就是容器会被清空。
而live_stack_在第2步中会和allocation_stack_交换,所以交换后,
allocation_stack_就是空容器了。
简单来说,live_stack_中的对象属于集合Live。但是,请读者注意,live_stack_只是集合
Live的一部分。因为它只保存了两次GC间创建的对象。*/
accounting::ObjectStack* live_stack = heap_->GetLiveStack();
/*调用Heap MarkAllocStackAsLive对live_stack中的元素进行处理。
(1) 这些元素就是一个个mirror Object对象,它们属于集合Live。
(2) MarkAllocStackAsLive将找到这些对象所在的空间,然后对这些空间对象的
live_bitmap_位图进行设置。也就是说,集合Live由空间对象的live_bitmap_表示。*/
heap_->MarkAllocStackAsLive(live_stack);
live_stack->Reset();//清空Heap live_stack_的内容
}
//遍历HeapContinuous_spaces_的成员,读者可回顾14.4.1节中的图14-6。
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
/*结合图14-6以及13.1节的内容可知,只有
"main rosalloc space"和"zygote / non moving space"这两个空间为
ContinuousMemMapAllocSpace。而".../boot.art"对应的ImageSpace属于
ContinuousSpace。 */
if (space->IsContinuousMemMapAllocSpace()) {
space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
......
//调用ContinuousMemMapAllocSpace的Sweep函数,swap_bitmaps值为false
RecordFree(alloc_space->Sweep(swap_bitmaps));
}
}
//回收DiscontinuousSpace对象中的垃圾,请读者自行阅读这部分代码
SweepLargeObjects(swap_bitmaps)
}
collector::ObjectBytePair ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps) {
/*Sweep的返回值类型为ObjectBytePair,它类似std的pair类,包含两个信息,第一个信息
是回收的垃圾对象的个数,第二个信息是回收的内存的字节数。*/
//获取空间的live_bitmap_和mark_bitmap_成员,它们分别代表集合Live和集合Mark
accounting::ContinuousSpaceBitmap* live_bitmap = GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = GetMarkBitmap();
//如果live_bitmap和mark_bitmap是同一个对象,则不需要清除
if (live_bitmap == mark_bitmap) {
return collector::ObjectBytePair(0, 0);
}
SweepCallbackContext scc(swap_bitmaps, this);
//交换live_bitmap和mark_bitmap的值。本次调用if的条件不满足
if (swap_bitmaps) {
std::swap(live_bitmap, mark_bitmap);
}
/*调用ContinuousSpaceBitmap的SweepWalk函数,它将扫描从Begin()开始,到End()
结束的这段内存空间。请读者注意SweepWalk的参数:
live_bitmap:代表集合Live。
mark_bitmap:代表集合Mark。
SweepWalk判断一个对象是否为垃圾对象的条件很简单。假设某个对象在两个位图中的索引是i,
那么,该对象是垃圾的条件是"live_bitmap[i] & ~mark_bitmap[i]"为true,即:
(1) 如果live_bitmap[i]为1,说明它属于集合Live。
(2) 如果mark_bitmap[i]为0,说明这个对象不属于集合Mark。
当条件1和2满足时,这个对象就是垃圾对象。
GetSweepCallback由子类实现,返回一个处理垃圾对象回调函数。SweepWalk每找到一个垃圾对象
都会调用这个回调函数进行处理。*/
accounting::ContinuousSpaceBitmap::SweepWalk(
*live_bitmap, *mark_bitmap, reinterpret_cast<uintptr_t>(Begin()),
reinterpret_cast<uintptr_t>(End()), GetSweepCallback(),
reinterpret_cast<void*>(&scc));
return scc.freed;
}
void MallocSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs,
void* arg) {
/*ContinuousSpaceBitmap SweepWalk找到垃圾对象后就会回调SweepCallback。
参数中的num_ptrs代表垃圾对象的个数,而**ptrs代表一个垃圾对象数组的起始地址。*/
SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg);
space::MallocSpace* space = context->space->AsMallocSpace();
Thread* self = context->self;
//回调时传入的信息,swap_bitmaps为false
if (!context->swap_bitmaps) {
accounting::ContinuousSpaceBitmap* bitmap = space->GetLiveBitmap();
//既然是垃圾对象,则需要将其从live_bitmap_中去除
for (size_t i = 0; i < num_ptrs; ++i) {
bitmap->Clear(ptrs[i]);
}
}
context->freed.objects += num_ptrs;
//RosAlloc和DlMallocSpace均实现了FreeList函数,用于释放一组对象的内存
context->freed.bytes += space->FreeList(self, num_ptrs, ptrs);
}
/*SwapBitmaps的作用其实很简单,就是交换集合Live和集合Mark在相关数据结构中对应的成员变量。我们以集合Live和集合Mark为目标来看待交换后的结果。
集合Live包含了此次GC中搜索到的对象。显然,它们构成了集合Live第二部分的内容——即上一次GC后剩下的对象。注意,本次GC的剩余对象将作为下一次GC中集合Live的内容。
集合Mark包含的信息是原集合Live去掉本次GC中的垃圾对象后的结果。*/
void GarbageCollector::SwapBitmaps() {
......
const GcType gc_type = GetGcType();
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
/*回顾13.7.1节的内容可知,ZygoteSpace的gc_retention_policy_取值为kGcRetention-
PolicyFullCollect,而BumpPointerSpace、RegionSpace、DlMallocSpace、
RosAllocSpace的gc_retention_policy_取值为kGcRetentionPolicyAlwaysCollect,
ImageSpace的gc_retention_policy_取值为kGcRetentionPolicyNeverCollect。
下面这个if条件中包含两个判断,其中的第二个判断说明只在MarkSweep的时候才处理ZygoteSpace
空间。而PartialMarkSweep以及StickMarkSweep均不需要处理它。
这也符合kGcTypeFull等回收策略的要求。 */
if( space->GetGcRetentionPolicy() ==
space::kGcRetentionPolicyAlwaysCollect
|| (gc_type == kGcTypeFull && space->GetGcRetentionPolicy() ==
space::kGcRetentionPolicyFullCollect)) {
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
if (live_bitmap != nullptr && live_bitmap != mark_bitmap) {
/*更新Heap live_bitmap_和mark_bitmap_数组中的元素,ReplaceBitmap第一个
参数为旧值,第二个参数为新值。其内部将先找到旧值所在的数组索引,然后将新值存储
到该索引位置上。下面这两行代码就是交换Heap live_bitmap_和mark_bitmap_
对应元素的信息。 */
heap_->GetLiveBitmap()->ReplaceBitmap(live_bitmap, mark_bitmap);
heap_->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap);
//交换space中live_bitmap_和mark_bitmap_。
space->AsContinuousMemMapAllocSpace()->SwapBitmaps();
}
}
}
......//对大内存对象的处理,和上面类似
}
void Heap::UnBindBitmaps() {
......
for (const auto& space : GetContinuousSpaces()) {
if (space->IsContinuousMemMapAllocSpace()) {
space::ContinuousMemMapAllocSpace* alloc_space =
space->AsContinuousMemMapAllocSpace();
//temp_bitmap_不为空,说明之前曾经调用过BindLiveToMarkBitmap
if (alloc_space->HasBoundBitmaps())
alloc_space->UnBindBitmaps();
}
}
}
space.cc
void ContinuousMemMapAllocSpace::UnBindBitmaps() {
//temp_bitmap_保存了原mark_bitmap_的内容,而mark_bitmap_保存了原live_bitmap_的内容
accounting::ContinuousSpaceBitmap* new_bitmap = temp_bitmap_.release();
//恢复Heap mark_bitmap_对应索引的内容
Runtime::Current()->GetHeap()->GetMarkBitmap()->ReplaceBitmap(
mark_bitmap_.get(), new_bitmap);
//恢复mark_bitmap_的内容
mark_bitmap_.reset(new_bitmap);
}
void MarkSweep::FinishPhase() {
......
mark_stack_->Reset();//清空MarkSweep mark_stack_的内容
Thread* const self = Thread::Current();
ReaderMutexLock mu(self, *Locks::mutator_lock_);
WriterMutexLock mu2(self, *Locks::heap_bitmap_lock_);
//清空空间对象mark_bitmap_,也就是GC结束后,集合Mark将被清空
heap_->ClearMarkedObjects();
}
partial_mark_sweep
class PartialMarkSweep : public MarkSweep {
public:
virtual GcType GetGcType() const OVERRIDE {
return kGcTypePartial; //回收策略
}
......
protected:
virtual void BindBitmaps() OVERRIDE;
......
};
void PartialMarkSweep::BindBitmaps() {
//调用父类的BindBitmaps,根据14.4.3.1节的介绍可知,ImageSpace将
//加入immune_spaces_
MarkSweep::BindBitmaps();
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
//根据13.1节的内容可知,只有ZygoteSpace空间的回收策略是
//kGcRetentionPolicyFullCollect。所以,下面这几行代码就是将ZygoteSpace加入
//immune_spaces_
if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
immune_spaces_.AddSpace(space);
}
}
}
sticky_mark_sweep
class StickyMarkSweep FINAL : public PartialMarkSweep {
//StickyMarkSweep派生自PartialMarkSweep
public:
GcType GetGcType() const OVERRIDE {
return kGcTypeSticky;
}
....
protected:
void BindBitmaps() OVERRIDE;
void MarkReachableObjects() OVERRIDE ;
void Sweep(bool swap_bitmaps) OVERRIDE;
......
};
void StickyMarkSweep::BindBitmaps() {
//StickyMarkSweep不处理ImageSpace和ZygoteSpace
PartialMarkSweep::BindBitmaps();
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
if (space->IsContinuousMemMapAllocSpace() &&
space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) {
//调用ContinuousMemMapAllocSpace的BindLiveToMarkBitmap函数,见下文解释
space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
}
}
//处理DiscontinuousSpace的情况,请读者自行阅读
......
}
void ContinuousMemMapAllocSpace::BindLiveToMarkBitmap() {
accounting::ContinuousSpaceBitmap* live_bitmap = GetLiveBitmap();
if (live_bitmap != mark_bitmap_.get()) {
accounting::ContinuousSpaceBitmap* mark_bitmap = mark_bitmap_.release();
/*下面这行代码的意思是更新Heap mark_bitmap_中原mark_bitmap所在的元素。
更新前,该元素的旧值为mark_bitmap,更新后该元素的新增为live_bitmap。
要理解这行代码的含义,需要读者明白两点:
(1) 根据上文对MarkSweep GC代码逻辑的介绍可知,空间对象的live_bitmap_就是本次
GC的集合Live。
(2) Heap mark_bitmap_为集合Mark。调用BindBitmaps的时候,标记工作还未开展,
所以集合Mark为空(集合Mark在上次GC的FinishPhase中被清空)。
结合1和2,对StickyMarkSweep来说,标记还没有开始做(BindBitmaps函数为GC的
准备工作),我们就已经把上次GC的幸存对象“标记”好了。所以,上次GC的幸存对象在本
次GC中将被保留。*/
Runtime::Current()->GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap);
//mark_bitmap的值保存到temp_bitmap_中
temp_bitmap_.reset(mark_bitmap);
//原live_bitmap的信息保存到mark_bitmap_中
mark_bitmap_.reset(live_bitmap);
}
}
void StickyMarkSweep::MarkReachableObjects() {
//mark_stack_被清空,这表示在MarkRoots中做过标记的对象不再需要。但集合Mark的
//信息却留了下来
mark_stack_->Reset();
//注意下面这个函数最后一个参数的取值为kCardDirty – 1
RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1);
}
void StickyMarkSweep::Sweep(bool swap_bitmaps ATTRIBUTE_UNUSED) {
/*SweepArray的第一个参数为Heap live_stack_。live_stack_包含了两次GC间所创建的
对象。它就是StickyMarkSweep中的集合Live。 */
SweepArray(GetHeap()->GetLiveStack(), false);
}
void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) {
Thread* self = Thread::Current();
/*sweep_array_free_buffer_mem_map_是MarkSweep的成员变量,在构造函数中创建
读者将它看作一块内存即可。下面这行代码将这块内存转成数组变量以方便后续代码的使用。
数据变量名为chunk_free_buffer,数组元素的类型为Object*。 */
mirror::Object** chunk_free_buffer = reinterpret_cast<mirror::Object**>(
sweep_array_free_buffer_mem_map_->BaseBegin());
size_t chunk_free_pos = 0;
......
/*allocations为输入参数,指向Heap live_stack_,读者将live_stack_看成一个数组
容器即可。下面的变量中,objects为这个数组的起始元素,count为数组的元素个数。 */
StackReference<mirror::Object>* objects = allocations->Begin();
size_t count = allocations->Size();
/*sweep_spaces是一个数组,用于保存此次GC所要扫描的空间对象。请读者注意,这段
代码中包含一个优化处理。根据注释可知,Heapnon_moving_space_中不太可能出现很多垃
圾对象,所以代码将把non_moving_space_放到sweep_spaces数组的最后。*/
std::vector<space::ContinuousSpace*>sweep_spaces;
space::ContinuousSpace* non_moving_space = nullptr;
for (space::ContinuousSpace* space : heap_->GetContinuousSpaces()) {
if (space->IsAllocSpace() &&!immune_spaces_.ContainsSpace(space)&&
space->GetLiveBitmap() != nullptr) {
if (space == heap_->GetNonMovingSpace()) {
//如果是Heap non_moving_space_,则先不加到sweep_spaces数组中
non_moving_space = space;
} else {
sweep_spaces.push_back(space);//将space保存到数组中
}
}
}
//如果存在non_moving_space,则将其加到数组的最后
if (non_moving_space != nullptr) {
sweep_spaces.push_back(non_moving_space);
}
//接下来开始处理垃圾对象,逐个空间处理
for (space::ContinuousSpace* space : sweep_spaces) {
//space和alloc_space指向的是同一个空间对象。只不过后续需要调用AllocSpace的
//FreeList函数释放内存,所以这里会先定义一个alloc_space变量
space::AllocSpace* alloc_space = space->AsAllocSpace();
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
......
//objects为Heap live_stack_容器的起始元素,count为容器的元素个数,main
StackReference<mirror::Object>* out = objects;
for (size_t i = 0; i <count; ++i) {
mirror::Object* const obj = objects[i].AsMirrorPtr();
//kUseThreadLocalAllocationStack为编译常量,默认为true
if (kUseThreadLocalAllocationStack&& obj == nullptr) {
continue;
}
//先判断space是否包含obj
if (space->HasAddress(obj)) {
//空间对象的mark_bitmap没有设置obj,所以obj是垃圾对象,main
if (!mark_bitmap->Test(obj)) {
/*kSweepArrayChunkFreeSize的值为0。我们找到一个垃圾对象后并不是马上就
清理它,而是先存起来,等攒到一定数量后再一起清理。所以,下面这段代
码的含义就很好理解了。kSweepArrayChunkFreeSize值为1024。 */
if (chunk_free_pos >= kSweepArrayChunkFreeSize) {
freed.objects += chunk_free_pos;
//释放一组垃圾对象的内存,main
freed.bytes += alloc_space->FreeList(self, chunk_free_pos,
chunk_free_buffer);
chunk_free_pos = 0;
}
//如果个数还未超过1024,先存起来
chunk_free_buffer[chunk_free_pos++] = obj;
}
} else {//对应!mark_bitmap->Test(obj)为false的时候,即obj不是垃圾对象
/*obj不是垃圾对象的话,则把obj存到Heap live_stack_新的位置上。随着垃圾对象
被清除,非垃圾对象将向前移动以填补垃圾对象所占据的位置,这样可减少后续其他空间
对象处理的工作量。当然,live_stack_的元素个数也需要相应调整。main*/
(out++)->Assign(obj);
}
}
......
count = out - objects;//调整live_stack_的元素个数
}
......//对Discontinuousspaces的处理
{
......
allocations->Reset();//清空Heap live_stack_的内容
}
sweep_array_free_buffer_mem_map_->MadviseDontNeedAndZero();
}
//PartialMarkSweep和StickyMarkSweep均未重载下面这个函数
virtual CollectorType GetCollectorType() const OVERRIDE {
//is_concurrent_为MarkSweep的成员变量,如果为true,则返回kCollectorTypeCMS
return is_concurrent_ ? kCollectorTypeCMS : kCollectorTypeMS;
}
virtual GcType GetGcType() const OVERRIDE {
return kGcTypeFull;//MarkSweep支持最强力度的GC策略
}