ART MarkSweep GC MarkingPhase

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



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值