ART MarkSweep GC MarkingPhase

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

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



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 飞利浦电视显示雪花片怎么办 电视无信号出雪花怎么办 电视打开都是雪花没有电视台怎么办 电视视频1无信号怎么办 新买的电视无信号怎么办 卫星有信号没有视频怎么办 户户通没有信号怎么办视频 雪花泥粘衣服上怎么办 遗产按份额处分判决后怎么办 宝马后驱车下雪天怎么办 宝马后驱车路滑怎么办 车子陷入泥地里怎么办 深圳居住证签注过期了怎么办 手机不能播放视频乱码了怎么办 被加密的视频无法观看怎么办 苹果5忘记id密码怎么办 七个月宝宝脾胃不好怎么办 八个月宝宝脾虚怎么办 七个月宝宝脾胃虚怎么办 海岛奇兵点错了怎么办 螳螂的脚断了怎么办 海岛奇兵打不过玩家怎么办 海岛奇兵资源满了怎么办 海岛奇兵杯越来越多打不玩家怎么办 海岛奇兵控杯技巧 杯数太高怎么办 海岛奇兵发现求救信号怎么办 海岛奇兵被打了怎么办 小鱼翅卡喉咙了怎么办 鱼翅卡在喉咙里怎么办 斗鱼身份证被使用怎么办 做的鱼丸太腥了怎么办 做鱼丸太稀了怎么办 斗鱼手机号换了怎么办 斗鱼直播掉帧怎么办 手机一直卡顿点不动怎么办呢 斗鱼直播分值底怎么办 斗鱼6000鱼丸怎么办卡 斗鱼直播没人看怎么办 淘宝直播间没人气怎么办 挂水了还是有热度怎么办 陌陌工会不结算工资怎么办