ART MarkSweep GC MarkingPhase

来源:互联网 发布:四维星装饰软件下载 编辑:程序博客网 时间:2024/06/16 00:56

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,这样的效率较高;比如:
  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;  }
在这段代码中,第一个 if 语句的命中率较高,减少查找搜寻操作。理论上是这个意思,具体实际效果如何,未做确认。

总结: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 table
1. 先看下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);}
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);}
实际上就是通过 Runtime 去标记一些全局的object。
  • 通过 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

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));    }  }}
主要是通过 image space/ zygote space 的 mod_uion_table 记录的 cleared_cards_ 和 card_bitmap_ 进行标记对应的 space的对象;

对于 App image ,则通过获取其 space的 live bitmap来标记对象;


2.RecursiveMark()

递归标记,主要代码是:

void MarkSweep::RecursiveMark() {  ProcessMarkStack(false);}
记得我们在 MarkRoots() 时,把 GCRoots 都保存在 mark_stack_中了,这里就通过这个 mark_stack_ 来标记  GC Roots 引用的对象。

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() 函数进行处理:

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();    }  }}
我们看到,这里会把各类 Reference类型的独享,放到各自的 reference queue中,会等到 GC Reclaim Phase 统一处理。

总结: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 流程总结:



原创粉丝点击