MarkingPhase 步骤
- BindBitmaps()
- FindDefaultSpaceBitmap()
- heap->ProcessCards()
- MarkRoots()
- MarkReachableObjects()
- PreCleanCards()
1.BindBitmaps()
Mark Object 是通过 bitmap 来标志整个 heap上的 objects是否被标记的。在 MarkSweep collector中,BindBitmaps()实现如下:void MarkSweep::BindBitmaps() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); // Mark all of the spaces we never collect as immune. for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect) { immune_spaces_.AddSpace(space); } } }
遍历了所有的 continuous space,如果这个 space 对应的GC回收策略是 kGcRetentionPolicyNeverCollect,则把这个 space 加入到GC豁免集合中;immune_spaces_ 是 ImmuneSpaces 类型对象。把 space 添加到 immune_spaces_的意思是,豁免space内的所有 object都不会被回收,在 GC过程中也不需要被 mark。
另外,需要说明的是:只有 ImageSpace的回收策略是 kGcRetentionPolicyNeverCollect,即永不回收。在ART中可能有一个或多个 image space,这是因为Android支持mutiImage,比如常见到的 boot.art, boot-framework.art, boot-***.art 都是作为ImageSpace添加到Heap中的。
我们看下 ImmuneSpace 的 AddSpace() 函数做了什么事情:void ImmuneSpaces::AddSpace(space::ContinuousSpace* space) { DCHECK(spaces_.find(space) == spaces_.end()) << *space; // Bind live to mark bitmap if necessary. if (space->GetLiveBitmap() != space->GetMarkBitmap()) { CHECK(space->IsContinuousMemMapAllocSpace()); space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap(); } spaces_.insert(space); CreateLargestImmuneRegion(); }
1.被豁免的 space 的 live_bitmap 如果不等于 mark_bitmap,会通过 BindLiveToMarkBitmap,把 mark_bitmap_ 设置为 live_bitmap_,并把 Heap中 mark_bitmap_ 中记录的该 space的 mark_bitmap_ 更改为记录 live_bitmap_。(这是因为 live_bitmap == mark_bitmap_时,再 MarkSweep的 Sweep函数中,就直接返回了)2.把这个这个space添加到 ImmuneSpace 的 set 数据集合 spaces_ 中;3.每添加一个 space,尝试重新为 ImmuneSpace 创建 ImmuneRegion,或者把 space 追加到 ImmuneRegion的末尾。这个过程可能会创建多个 ImmuneReion,但最终我们使用最大的那个。用来做 fast path lookup,这样的效率较高;比如:在这段代码中,第一个 if 语句的命中率较高,减少查找搜寻操作。理论上是这个意思,具体实际效果如何,未做确认。ALWAYS_INLINE bool ContainsObject(const mirror::Object* obj) const { if (largest_immune_region_.ContainsObject(obj)) { return true; } for (space::ContinuousSpace* space : spaces_) { if (space->HasAddress(obj)) { return true; } } return false; }
总结:BindBitmaps() 主要就是设置 ImageSpace 不被回收,GC 时也不用标记,并设置 ImmuneSpace,ImmuneRegion。
2.FindDefaultSpaceBitmap()
这个函数的功能是为了找出 main space 中的 Bitmap作为 default 的bitmap,实际也是为了提高效率,因为我么知道,GC 回收,实际回收最多的就是main space,它是GC 回收的主要目标,所以把它的 bitmap作为 default 可以提升效率。代码如下:void MarkSweep::FindDefaultSpaceBitmap() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); for (const auto& space : GetHeap()->GetContinuousSpaces()) { accounting::ContinuousSpaceBitmap* bitmap = space->GetMarkBitmap(); // We want to have the main space instead of non moving if possible. if (bitmap != nullptr && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { current_space_bitmap_ = bitmap; // If we are not the non moving space exit the loop early since this will be good enough. if (space != heap_->GetNonMovingSpace()) { break; } } } CHECK(current_space_bitmap_ != nullptr) << "Could not find a default mark bitmap\n" << heap_->DumpSpaces(); }
ART Heap中回收策略为 kGcRetentionPolicyAlwaysCollect 的space类型有 BumpPointerSpace,MallocSpace(子类 DlMallocSpace,RosAllocSpace),RegionSpace,LargeObjectSpace。其中 BumpPointerSpace 和 RegionSpace 没有使用,而 LargeObjectSpace是 DisContinuousSpace,所以这里满足条件的只有 DlMallocSpace 和 RosAllocSpace 类型的 space。而实际上 main_space_ 是 RosAllocSpace,并会被设置为 heap->rosalloc_space_ = main_space_.non_moving_space_ 实际上是 DlMallocSpace 类型的,注释里说 non_moving_space_ 必须是 DlMallocSpace,因为当前不支持多个活动的 RosAllocSpace(原因待调查)。另外,main_space_backup_ 实际上是 RosAllocSpace,所以说,main_space_backup_不会和 main_space_ 同时活动的。main_space_backup_在创建的时候是先通过AddSpace()添加,后又条用 RemoveSpace()删除掉了。(为什么添加又删除 ?)
总结:FindDefaultSpaceBitmap() 主要是为了 GC 的效率考虑,把最常访问的,最容易命中的 main_space_ 的 bitmap作为 current_space_bitmap_.
3.heap->ProcessCards()
这一步主要是为了处理 ZygoteSpace 和 ImageSpace 的 ModUnionTable,以及清空 AllocSapce 的 CardTable。还是看代码:void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards, bool clear_alloc_space_cards) { TimingLogger::ScopedTiming t(__FUNCTION__, timings); // Clear cards and keep track of cards cleared in the mod-union table. for (const auto& space : continuous_spaces_) { accounting::ModUnionTable* table = FindModUnionTableFromSpace(space); accounting::RememberedSet* rem_set = FindRememberedSetFromSpace(space); if (table != nullptr) { const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" : "ImageModUnionClearCards"; TimingLogger::ScopedTiming t2(name, timings); table->ProcessCards(); } else if (use_rem_sets && rem_set != nullptr) { DCHECK(collector::SemiSpace::kUseRememberedSet && collector_type_ == kCollectorTypeGSS) << static_cast<int>(collector_type_); TimingLogger::ScopedTiming t2("AllocSpaceRemSetClearCards", timings); rem_set->ClearCards(); } else if (process_alloc_space_cards) { TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings); if (clear_alloc_space_cards) { uint8_t* end = space->End(); if (space->IsImageSpace()) { // Image space end is the end of the mirror objects, it is not necessarily page or card // aligned. Align up so that the check in ClearCardRange does not fail. end = AlignUp(end, accounting::CardTable::kCardSize); } card_table_->ClearCardRange(space->Begin(), end); } else { // No mod union table for the AllocSpace. Age the cards so that the GC knows that these // cards were dirty before the GC started. // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread) // -> clean(cleaning thread). // The races are we either end up with: Aged card, unaged card. Since we have the // checkpoint roots and then we scan / update mod union tables after. We will always // scan either card. If we end up with the non aged card, we scan it it in the pause. card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), VoidFunctor()); } } } }
当collector是 MarkSweep时,ProcessCards的参数是 (timing,fase,true,true)。所以会走如下逻辑:
ZygoteSpace 和 ImageSpace 会调用各自 ModUnionTable 的 ProcessCards() 函数 Alloc Space 会清空它们的范围对应的 card table1. 先看下ImageSpace的 ModUnionTable的 ProcessCards 做了什么:void ModUnionTableReferenceCache::ProcessCards() { CardTable* card_table = GetHeap()->GetCardTable(); ModUnionAddToCardSetVisitor visitor(&cleared_cards_); // Clear dirty cards in the this space and update the corresponding mod-union bits. card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor); }
在这个函数里主要做了以下事情:
通过AgeCardVisitor 把当前ImageSpace对应的所有 dirty card(0x70)清除(修改为 0x6f),非 dirty card都设置为 clean card(0x0) 这些 dirty card 被清除后,会记录在当前 ModUionTable的成员 CardSet cleared_cards_中,这个 cleared_cards_是用来 update mod-union table的,具体怎么update,等待调查2. ZygoteSpace的 ModUnionTable 的 ProcessCards 函数:void ModUnionTableCardCache::ProcessCards() { CardTable* const card_table = GetHeap()->GetCardTable(); ModUnionAddToCardBitmapVisitor visitor(card_bitmap_.get(), card_table); // Clear dirty cards in the this space and update the corresponding mod-union bits. card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor); }
这个函数中也是做了两件事情:
通过 AgeCardVisitor 把 ZygoteSpace 对应的 .....(同上) 会使用当前 ModUnionTable 的 CardBitmap card_bitmap_ 把这些被从 dirty 状态改到 0x6f 状态的 card 记录下来,据说也是用来 update mod-union table的3. 清空 Alloc Space 对应的 CardTable:void CardTable::ClearCardRange(uint8_t* start, uint8_t* end) { CHECK_ALIGNED(reinterpret_cast<uintptr_t>(start), kCardSize); CHECK_ALIGNED(reinterpret_cast<uintptr_t>(end), kCardSize); static_assert(kCardClean == 0, "kCardClean must be 0"); uint8_t* start_card = CardFromAddr(start); uint8_t* end_card = CardFromAddr(end); ZeroAndReleasePages(start_card, end_card - start_card); }
这个函数把space 对应的 card table内存,整page的部分 madvise DONT_NEEDED,非整部分的内存填充为 0x0.
总结:heap->ProcessCards 清理 ImageSpace和ZygoteSpace对应的 card table,并记录下来;清零 Alloc space对应的 card table,并建议回收 card table 对应内存。
4.MarkRoots()
真正的 Mark 来了,其实前面3点都还在为 Mark 做准备,在 MarkRoots() 函数里,会 Mark 当前虚拟机中的所有 GC Root;它分为如下几个部分:
MarkRootsCheckpoint(self, kRevokeRosAllocThreadLocalBuffersAtCheckpoint); MarkNonThreadRoots(); MarkConcurrentRoots(
static_cast<VisitRootFlags>(kVisitRootFlagAllRoots | kVisitRootFlagStartLoggingNewRoots));
4.1 MarkRootsCheckpoint() 通过 RunCheckpoint() 标记所有 thread 对应的 GC Root,并撤回所有线程的 Thread local buffer
这里我们主要看下 Mark Thread roots 使用的 Checkpoint 的是CheckpointMarkThreadRoots,主要实现:virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { ScopedTrace trace("Marking thread roots"); // Note: self is not necessarily equal to thread since thread may be suspended. Thread* const self = Thread::Current(); CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; thread->VisitRoots(this); if (revoke_ros_alloc_thread_local_buffers_at_checkpoint_) { ScopedTrace trace2("RevokeRosAllocThreadLocalBuffers"); mark_sweep_->GetHeap()->RevokeRosAllocThreadLocalBuffers(thread); } // If thread is a running mutator, then act on behalf of the garbage collector. // See the code in ThreadList::RunCheckpoint. mark_sweep_->GetBarrier().Pass(self); }
对于 Runnable状态的线程执行到 Suspend point的时候,会自动执行这个 Run 函数,对于 Suspended 状态的线程,会由当前 GC 线程帮助对其进行 VisitRoots(RootVistor v)操作,由于当前RootVisitor 的功能是Mark,所以在Visit之后,就会Mark当前的 Thread Roots.然后撤回当前 Thread 的 RosAllocThreadLocalBuffers。
下面我们来看下每个线程的 Thread Roots 是如何来进行 Visit的:void Thread::VisitRoots(RootVisitor* visitor) { const uint32_t thread_id = GetThreadId(); visitor->VisitRootIfNonNull(&tlsPtr_.opeer, RootInfo(kRootThreadObject, thread_id)); if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) { visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), RootInfo(kRootNativeStack, thread_id)); } visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id)); tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id)); tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id)); HandleScopeVisitRoots(visitor, thread_id); if (tlsPtr_.debug_invoke_req != nullptr) { tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id)); } // Visit roots for deoptimization. if (tlsPtr_.stacked_shadow_frame_record != nullptr) { RootCallbackVisitor visitor_to_callback(visitor, thread_id); ReferenceMapVisitor<RootCallbackVisitor, kPrecise> mapper(this, nullptr, visitor_to_callback); for (StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record; record != nullptr; record = record->GetLink()) { for (ShadowFrame* shadow_frame = record->GetShadowFrame(); shadow_frame != nullptr; shadow_frame = shadow_frame->GetLink()) { mapper.VisitShadowFrame(shadow_frame); } } } for (DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack; record != nullptr; record = record->GetLink()) { if (record->IsReference()) { visitor->VisitRootIfNonNull(record->GetReturnValueAsGCRoot(), RootInfo(kRootThreadObject, thread_id)); } visitor->VisitRootIfNonNull(record->GetPendingExceptionAsGCRoot(), RootInfo(kRootThreadObject, thread_id)); } if (tlsPtr_.frame_id_to_shadow_frame != nullptr) { RootCallbackVisitor visitor_to_callback(visitor, thread_id); ReferenceMapVisitor<RootCallbackVisitor, kPrecise> mapper(this, nullptr, visitor_to_callback); for (FrameIdToShadowFrame* record = tlsPtr_.frame_id_to_shadow_frame; record != nullptr; record = record->GetNext()) { mapper.VisitShadowFrame(record->GetShadowFrame()); } } for (auto* verifier = tlsPtr_.method_verifier; verifier != nullptr; verifier = verifier->link_) { verifier->VisitRoots(visitor, RootInfo(kRootNativeStack, thread_id)); } // Visit roots on this thread's stack Context* context = GetLongJumpContext(); RootCallbackVisitor visitor_to_callback(visitor, thread_id); ReferenceMapVisitor<RootCallbackVisitor, kPrecise> mapper(this, context, visitor_to_callback); mapper.template WalkStack<StackVisitor::CountTransitions::kNo>(false); ReleaseLongJumpContext(context); for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { visitor->VisitRootIfNonNull(&frame.this_object_, RootInfo(kRootVMInternal, thread_id)); } }
看第一个 : visitor->VisitRootIfNonNull(&tlsPtr_.opeer,..); 由于当前 visitor是 CheckpointMarkThreadRoots,继承自 RootVisitor,其这个函数 VisitRootIfNonNull()是在 RootVisitor中实现的,基本执行流程如下图:
简单来讲,这个流程就是 mark 一个 obj 到对应 bitmap 的 bit 位,并把它 push 到 mark_stack_ ,以便递归标记子节点。
接下来看,Thread::VisitRoots(),这个函数里包含了一个 Thread 的所有需要被 Visit的内容,下面是一个thred中需要被标记的内容:
总结:MarkRootsCheckPoint() 就是要把当前进程所有 thread 所必须的一些对象标记上,这些对象是 GCRoot,当从 mark_stack_递归标记时,就相当于从根集对象 GCRoots 依次标记他们引用的对象了。
RevokeRosAllocThreadLocalBuffers就不再细说了。
4.2 MarkNonThreadRoots();
看代码:void MarkSweep::MarkNonThreadRoots() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Runtime::Current()->VisitNonThreadRoots(this); }
实际上就是通过 Runtime 去标记一些全局的object。void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { java_vm_->VisitRoots(visitor); sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); verifier::MethodVerifier::VisitStaticRoots(visitor); VisitTransactionRoots(visitor); }
- 通过 java_vm_ 标记 Global Reference Table中所有的 IndirectReference
- 标记全局对象 sentinel_
- 标记全局 Throwable对象 pre_allocated_OutOfMemoryError_
- 标记全局 Throwable对象 pre_allocated_NoClassDefFoundError_
- 标记基础对象类型,比如ShortType, IntegerType
- 当在一个 transaction 过程时,需要 mark transaction 中的对象
4.3 MarkConcurrentRoots()
void MarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Visit all runtime roots and clear dirty flags. Runtime::Current()->VisitConcurrentRoots(this, flags); }
void Runtime::VisitConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) { intern_table_->VisitRoots(visitor, flags); class_linker_->VisitRoots(visitor, flags); heap_->VisitAllocationRecords(visitor); if ((flags & kVisitRootFlagNewRoots) == 0) { // Guaranteed to have no new roots in the constant roots. VisitConstantRoots(visitor); } Dbg::VisitRoots(visitor); }
通过 Runtime 标记全局对象:
- 标记intern_table_ 中 strong_interns_包含的所有 String对象
- classlinker 标记 class_roots_ array 对象,interface table对象,boot_class_table_中的class,strong_roots_(dexfile或者dexcache), oat_files_; 标记所有 class_loaders
- heap 这个是为了在 alloc track开启的情况下,标记 record信息
- Visit flag设置 kVisitRootFlagNewRoots的情况下,标记 constant roots
- Dbg 相关的对象的标记
总结:MarkRoots() 就是为了标记不能被回收的根集对象,并 push 到 mark_stack_中,后面才能根据这些对象递归的标记可达对象,简单的理解,这些标记完成后,就可以回收那些没有被标记的对象了。
5.MarkReachableObjects()
在上面标记完根集对象之后,就可以递归标记可达对象了。
void MarkSweep::MarkReachableObjects() { UpdateAndMarkModUnion(); // Recursively mark all the non-image bits set in the mark bitmap. RecursiveMark(); }
1.UpdateAndMarkModUnion主要是通过 image space/ zygote space 的 mod_uion_table 记录的 cleared_cards_ 和 card_bitmap_ 进行标记对应的 space的对象;void MarkSweep::UpdateAndMarkModUnion() { for (const auto& space : immune_spaces_.GetSpaces()) { const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable"; DCHECK(space->IsZygoteSpace() || space->IsImageSpace()) << *space; TimingLogger::ScopedTiming t(name, GetTimings()); accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space); if (mod_union_table != nullptr) { mod_union_table->UpdateAndMarkReferences(this); } else { // No mod-union table, scan all the live bits. This can only occur for app images. space->GetLiveBitmap()->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), reinterpret_cast<uintptr_t>(space->End()), ScanObjectVisitor(this)); } } }
对于 App image ,则通过获取其 space的 live bitmap来标记对象;
2.RecursiveMark()
递归标记,主要代码是:
记得我们在 MarkRoots() 时,把 GCRoots 都保存在 mark_stack_中了,这里就通过这个 mark_stack_ 来标记 GC Roots 引用的对象。void MarkSweep::RecursiveMark() { ProcessMarkStack(false); }
ProcessMarkStack() 可以多个 thread 并行,也可以一个thread执行,总的原理就是:
MarkVisitor mark_visitor(this); DelayReferenceReferentVisitor ref_visitor(this); ScanObjectVisit(obj, mark_visitor, ref_visitor);
template<typename MarkVisitor, typename ReferenceVisitor> inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor, const ReferenceVisitor& ref_visitor) { DCHECK(IsMarked(obj)) << "Scanning unmarked object " << obj << "\n" << heap_->DumpSpaces(); obj->VisitReferences(visitor, ref_visitor); }
通过 ScanObjectVisit() 来Visit 目标 obj:
- 首先标记 obj 对象的 klass 对象
- 如果其 klass 是个 kClassFlagNormal,则使用 VisitInstanceFieldsReferences() 标记当前 obj 的引用对象
- kClassFlagClass 类型的class,单独进行处理
- kClassFlagObjectArray 类型的class,单独进行处理
- kClassFlagReference 类型的class,首先通过 VisitInstanceFieldsReferences标记引用对象,然后通过 DelayReferenceReferentVisitor 处理此类对象
- kClassFlagDexCache 和 class loader类型的对象,也单独进行标记
看下 Reference类型对象的处理是通过 ReferenceProcessor 的 DelayReferenceReferent() 函数进行处理:
我们看到,这里会把各类 Reference类型的独享,放到各自的 reference queue中,会等到 GC Reclaim Phase 统一处理。void ReferenceProcessor::DelayReferenceReferent(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref, collector::GarbageCollector* collector) { mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr(); if (!collector->IsNullOrMarkedHeapReference(referent, /*do_atomic_update*/true)) { if (klass->IsSoftReferenceClass()) { soft_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref); } else if (klass->IsWeakReferenceClass()) { weak_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref); } else if (klass->IsFinalizerReferenceClass()) { finalizer_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref); } else if (klass->IsPhantomReferenceClass()) { phantom_reference_queue_.AtomicEnqueueIfNotEnqueued(self, ref); } else { LOG(FATAL) << "Invalid reference type " << klass->PrettyClass() << " " << std::hex << klass->GetAccessFlags(); } } }
总结:MarkReachableObjects() 目的就是递归标记 MarkRoots() 阶段找出来的 GCRoot根集对象,其中对不同的 类型的 Object会做一些特殊处理,比如 Reference类型的 obj。
6.PreCleanCards()
这一步的官方注释是: // Pre-clean dirtied cards to reduce pauses.
意思就是在 MarkingPhase 函数结束的时候,其实已经过去了一小段时间,如果是 concurrnet GC,那么这段时间,一些对象的访问,修改等,应该已经产生了一些 Dirty card。如果不是 concurrent GC则不会,那么 PreCleanCards() 函数里不会再执行。
在 concurrent GC的情况,我们在这里,先通过 PreCleanCards() 来处理一些 dirty card,以减少后面 pause阶段的工作,这样 pause 阶段时间减少,能够更好的减少卡顿。实现如下:
void MarkSweep::PreCleanCards() { // Don't do this for non concurrent GCs since they don't have any dirty cards. if (kPreCleanCards && IsConcurrent()) { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Thread* self = Thread::Current(); CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)); // Process dirty cards and add dirty cards to mod union tables, also ages cards. heap_->ProcessCards(GetTimings(), false, true, false); // The checkpoint root marking is required to avoid a race condition which occurs if the // following happens during a reference write: // 1. mutator dirties the card (write barrier) // 2. GC ages the card (the above ProcessCards call) // 3. GC scans the object (the RecursiveMarkDirtyObjects call below) // 4. mutator writes the value (corresponding to the write barrier in 1.) // This causes the GC to age the card but not necessarily mark the reference which the mutator // wrote into the object stored in the card. // Having the checkpoint fixes this issue since it ensures that the card mark and the // reference write are visible to the GC before the card is scanned (this is due to locks being // acquired / released in the checkpoint code). // The other roots are also marked to help reduce the pause. MarkRootsCheckpoint(self, false); MarkNonThreadRoots(); MarkConcurrentRoots( static_cast<VisitRootFlags>(kVisitRootFlagClearRootLog | kVisitRootFlagNewRoots)); // Process the newly aged cards. RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1); // TODO: Empty allocation stack to reduce the number of objects we need to test / mark as live // in the next GC. } }
MarkingPhase 流程总结: