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 流程總結: