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



發佈了34 篇原創文章 · 獲贊 13 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章