1. 程式人生 > >ART執行時Mark-Compact( MC)GC執行過程分析

ART執行時Mark-Compact( MC)GC執行過程分析

       除了Semi-Space(SS)GC和Generational Semi-Space(GSS)GC,ART執行時還引入了第三種Compacting GC:Mark-Compact(MC)GC。這三種GC雖然都是Compacting GC,不過它們的實現方式卻有很大不同。SS GC和GSS GC需兩個Space來壓縮記憶體,而MC GC只需一個Space來壓縮記憶體。本文就詳細分析MC GC的執行過程。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!


圖1 Mark-Compact GC的執行過程

       從圖1可以看出,當Mark-Compact GC執行完成之後,原來位於Bump Pointer Space上的仍然存活的物件會被依次移動至原Bump Pointer Space的左側,並且按地址從小到大緊湊地排列在一起。這個過程不需要藉助於額外的Space來完成。這一點是Mark-Compact GC與Semi-Space GC、Generational Semi-Space GC的顯著區別。

       不過,Mark-Compact GC與Semi-Space GC、Generational Semi-Space GC一樣,除了需要對ART執行時當前使用的Bump Pointer Space進行內在壓縮之外,還需要修改其它Space對Bump Pointer Space的引用,因為Bump Pointer Space的物件發生了移動。此外,ART執行時堆的Non Moving Space和Large Object Space也會進行像Mark-Sweep GC一樣的垃圾回收。

       從前面ART執行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析

一文還可以知道,在ART執行時內部,所有的GC都是通過Heap類的成員函式CollectGarbageInternal開始執行的,並且當決定要執行Mark-Compact GC時,最終會以MarkCompact類的成員函式RunPhases作為入口點,如下所示:

void MarkCompact::RunPhases() {
  Thread* self = Thread::Current();
  InitializePhase();
  CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
  {
    ScopedPause pause(this);
    ......
    MarkingPhase();
    ReclaimPhase();
  }
  ......
  FinishPhase();
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       與Semi-Space GC、Generational Semi-Space GC一樣,Mark-Compact GC的執行過程也分為初始化、標記、回收和結束四個階段,對應的函式分別為MarkCompact類的成員函式InitializePhase、MarkingPhase、ReclaimPhase和FinishPhase。其中,標記和回收階段是在掛起其它的ART執行時執行緒的前提下進行的。注意,掛起其它的ART執行時執行緒的操作通過ScopedPause類的建構函式實現的。當標記和回收階段結束,ScopedPause類的解構函式就會自動恢復之前被掛起的ART執行時執行緒。

       接下來,我們就分別分析Mark-Compact GC的四個階段的執行過程,即MarkCompact類的成員函式InitializePhase、MarkingPhase、ReclaimPhase和FinishPhase的實現。

       Mark-Compact GC的初始化階段由MarkCompact類的成員函式InitializePhase實現,如下所示:

void MarkCompact::InitializePhase() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  mark_stack_ = heap_->GetMarkStack();
  ......
  immune_region_.Reset();
  ......
  // TODO: I don't think we should need heap bitmap lock to Get the mark bitmap.
  ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
  mark_bitmap_ = heap_->GetMarkBitmap();
  live_objects_in_space_ = 0;
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式InitializePhase主要就是執行一些初始化工作,例如獲得ART執行時堆的Mark Stack、Mark Bitmap,儲存在成員變數mark_stack_和mark_bitmap_中,並且重置MarkCompact類的成員變數immune_region_描述的一個不進行垃圾回收的Space區間為空,以及將成員變數live_objects_in_space_的值置為0。MarkCompact類的成員變數live_objects_in_space_用來描述Mark-Compact GC執先完成後,Bump Pointer Space還有多少物件是存活的。

       Mark-Compact GC的標記階段來MarkCompact類的成員函式MarkingPhase實現,如下所示:

void MarkCompact::MarkingPhase() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  Thread* self = Thread::Current();
  // Bitmap which describes which objects we have to move.
  objects_before_forwarding_.reset(accounting::ContinuousSpaceBitmap::Create(
      "objects before forwarding", space_->Begin(), space_->Size()));
  // Bitmap which describes which lock words we need to restore.
  objects_with_lockword_.reset(accounting::ContinuousSpaceBitmap::Create(
      "objects with lock words", space_->Begin(), space_->Size()));
  CHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
  // Assume the cleared space is already empty.
  BindBitmaps();
  t.NewTiming("ProcessCards");
  // Process dirty cards and add dirty cards to mod-union tables.
  heap_->ProcessCards(GetTimings(), false);
  // Clear the whole card table since we can not Get any additional dirty cards during the
  // paused GC. This saves memory but only works for pause the world collectors.
  t.NewTiming("ClearCardTable");
  heap_->GetCardTable()->ClearCardTable();
  // Need to do this before the checkpoint since we don't want any threads to add references to
  // the live stack during the recursive mark.
  if (kUseThreadLocalAllocationStack) {
    t.NewTiming("RevokeAllThreadLocalAllocationStacks");
    heap_->RevokeAllThreadLocalAllocationStacks(self);
  }
  t.NewTiming("SwapStacks");
  heap_->SwapStacks(self);
  {
    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
    MarkRoots();
    // Mark roots of immune spaces.
    UpdateAndMarkModUnion();
    // Recursively mark remaining objects.
    MarkReachableObjects();
  }
  ProcessReferences(self);
  {
    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
    SweepSystemWeaks();
  }
  // Revoke buffers before measuring how many objects were moved since the TLABs need to be revoked
  // before they are properly counted.
  RevokeAllThreadLocalBuffers();
  ......
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式MarkingPhase的執行過程如下所示:

       1. 針對當前要進行Mark-Compact的Bump Pointer Space,建立兩個ContinuousSpaceBitmap,分別儲存在MarkCompact類的成員變數objects_before_forwarding_和objects_with_lockword_。其中,前者用來記錄當前要進行Mark-Compact的Bump Pointer Space的存活物件,而後者用來記錄上述的存活物件在移動前它們的LockWord有沒有被覆蓋。如果被覆蓋了,那麼在它們移動之後,需要恢復它們之前的LockWord。後面我們就可以看到,Mark-Compact GC在移動物件的時候,會先計算出每一個需要移動的物件的新地址,並且將該新地址作為一個Forwarding Address記錄它的LockWord中。因此,當物件移動到新地址後,就需要恢復它們之前的LockWord值。

       2. 呼叫MarkCompact類的成員函式BindBitmaps確定哪些Space是不需要進行垃圾回收的。

       3. 呼叫Heap類的成員函式ProcessCards處理Dirty Card,即將它們記錄在對應的Mod Union Table中,以便後面可以找到上次GC以來,不需要進行垃圾回收的Space對需要垃圾回收的Space的引用情況。

       4. 呼叫CardTable類的成員函式ClearCardTable清零Dirty Card。因為Dirty Card在經過上一步的操作之後,已經記錄在了對應的Mod Union Table,因此現在不需要它們了。

       5. 如果ART執行時堆使用了執行緒區域性Allocation Stack,即在常量kUseThreadLocalAllocationStack等於true的情況下,呼叫Heap類的成員函式RevokeAllThreadLocalAllocationStacks對它們進行回收。

       6. 呼叫Heap類的成員函式SwapStacks交換ART執行時堆的Allocation Stack和Live Stack。

       7. 呼叫MarkCompact類的成員函式MarkRoots標記根集物件。

       8. 呼叫MarkCompact類的成員函式UpdateAndMarkModUnion標記Dirty Card引用的物件。

       9. 呼叫MarkCompact類的成員函式MarkReachableObjects標記可達物件,即遞迴標記根集物件和Dirty Card引用的物件可達的物件。

       10. 呼叫MarkCompact類的成員函式ProcessReferences處理引用型別的物件,即Soft Reference、Weak Reference、Phantom Reference和Finalizer Reference物件。

       11. 呼叫MarkCompact類的成員函式SweepSystemWeaks處理那些沒有被標記的常量字串、Monitor物件和在JNI建立的全域性弱引用物件等。

       12. 呼叫MarkCompact類的成員函式RevokeAllThreadLocalBuffers回收各個ART執行時執行緒的區域性分配緩衝區。

       在上述的操作中,我們主要是分析第2、7、8和9操作,即MarkCompact類的成員函式BindBitmaps、MarkRoots、UpdateAndMarkModUnion和MarkReachableObjects的實現、其它的操作可以參考ART執行時垃圾收集(GC)過程分析或者ART執行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析一文。

       MarkCompact類的成員函式BindBitmaps的實現如下所示:

void MarkCompact::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 ||
        space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
      CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space;
    }
  }
}

       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       在前面ART執行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析這篇文章提到,在ART執行時中,回收策略為kGcRetentionPolicyNeverCollect和kGcRetentionPolicyFullCollect的Space是Image Space和Zygote Space,因此,從這裡可以看出,在Mark-Compact GC中,Image Space和Zygote Space是不進行垃圾回收的,同時也意味著Non Moving Space、Bump Pointer Space和Large Object Space需要進行垃圾回收。

       MarkCompact類的成員函式MarkRoots的實現如下所示:

void MarkCompact::MarkRoots() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  Runtime::Current()->VisitRoots(MarkRootCallback, this);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式MarkRoots呼叫Runtime類的成員函式VisitRoots遍歷當前的根集物件,並且對於每一個根集物件,都呼叫MarkCompact類的靜態成員函式MarkRootCallback進行標記處理。

       MarkCompact類的靜態成員函式MarkRootCallback的實現如下所示:

void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
                                   RootType /*root_type*/) {
  reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的靜態成員函式MarkRootCallback又是通過呼叫另外一個成員函式MarkObject來標記根集物件,如下所示:

inline void MarkCompact::MarkObject(mirror::Object* obj) {
  ......
  if (immune_region_.ContainsObject(obj)) {
    return;
  }
  if (objects_before_forwarding_->HasAddress(obj)) {
    if (!objects_before_forwarding_->Set(obj)) {
      MarkStackPush(obj);  // This object was not previously marked.
    }
  } else {
    ......
    BitmapSetSlowPathVisitor visitor;
    if (!mark_bitmap_->Set(obj, visitor)) {
      // This object was not previously marked.
      MarkStackPush(obj);
    }
  }
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式MarkObject首先判斷物件obj是否位於非垃圾回收空間中。如果是的話,那麼不用對它進行處理了。

       MarkCompact類的成員函式MarkObject接著通過成員變數objects_before_forwarding_指向的一個ContinuousSpaceBitmap來判斷物件obj是否位於要進行垃圾回收和物件壓縮的Bump Pointer Space中。如果是的話,再將其在上述的ContinuousSpaceBitmap的對應位設定為1。此外,如果物件obj是第一次被標記,那麼它還被壓入到Mark Stack中去,以便後面可以繼續對它引用的其它物件進行遞迴標記處理。

       如果物件obj不是位於要行垃圾回收和物件壓縮的Bump Pointer Space,那麼這時候它肯定就是在Non Moving Space和Large Object Space中,這時候就直接物件在Mark Bitmap上對應的位設定為1。同樣,如果物件是第一次被標記,那麼也會被壓入到Mark Stack中。

       根集物件標記完成之後,接下來再標記Dirty Card引用的物件,這是通過呼叫MarkCompact類的成員函式UpdateAndMarkModUnion實現的,如下所示:

void MarkCompact::UpdateAndMarkModUnion() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  for (auto& space : heap_->GetContinuousSpaces()) {
    // If the space is immune then we need to mark the references to other spaces.
    if (immune_region_.ContainsSpace(space)) {
      accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
      if (table != nullptr) {
        // TODO: Improve naming.
        TimingLogger::ScopedTiming t(
            space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" :
                                     "UpdateAndMarkImageModUnionTable", GetTimings());
        table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this);
      }
    }
  }
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       由於在Mark-Compact GC中,只有Image Space和Zygote Space是不需要進行垃圾回收的,因此這裡就僅僅對它們進行處理。在前面ART執行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析這篇文章提到,Image Space和Zygote Space都是有一個關聯的Mod Union Table,並且通過呼叫這個Mod Union Table的成員函式UpdateAndMarkReferences來處理Dirty Card引用的物件。

       對於Dirty Card引用的每一個物件,即Dirty Card記錄的上次GC以來有修改過引用型別的成員變數的物件,都會被MarkCompact類的靜態成員函式MarkHeapReferenceCallback進行標記,如下所示:

void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr,
                                          void* arg) {
  reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr());
}
      這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

      從這裡就可以看出,Dirty Card引用的物件與根集物件一樣,都是通過MarkCompact類的成員函式MarkObject進行標記的。

      Dirty Card引用的物件與根集物件都標記完成之後,就開始標記它們可達的物件了,這是通過呼叫MarkCompact類的成員函式MarkReachableObjects來實現的,如下所示:

void MarkCompact::MarkReachableObjects() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  accounting::ObjectStack* live_stack = heap_->GetLiveStack();
  {
    TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings());
    heap_->MarkAllocStackAsLive(live_stack);
  }
  live_stack->Reset();
  // Recursively process the mark stack.
  ProcessMarkStack();
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       與Semi-Space GC和Generational Semi-Space GC一樣,在遞迴標記根集物件和Dirty Card引用的物件的可達物件之前,首先會將儲存在Allocation Stack裡面的物件的Live Bitmap位設定為1。

       接下來,MarkCompact類的成員函式MarkReachableObjects呼叫另外一個成員函式ProcessMarkStack來遞迴標記根集物件和Dirty Card引用的物件的可達物件,後者的實現如下所示:

void MarkCompact::ProcessMarkStack() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  while (!mark_stack_->IsEmpty()) {
    Object* obj = mark_stack_->PopBack();
    DCHECK(obj != nullptr);
    ScanObject(obj);
  }
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式ProcessMarkStack逐個地將Mark Stack的物件彈出來,並且呼叫另外一個成員函式ScanObject對它們的引用型別的成員變數進行標記。注意, MarkCompact類的成員函式ScanObject在標記物件的過程中,也會可能壓入新的物件到Mark Stack中,因此這是一個遞迴標記的過程。

       MarkCompact類的成員函式ScanObject的實現如下所示:

void MarkCompact::ScanObject(Object* obj) {
  MarkCompactMarkObjectVisitor visitor(this);
  obj->VisitReferences<kMovingClasses>(visitor, visitor);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       從這裡可以看到,物件obj的引用型別的成員變數引用的物件是通過MarkCompactMarkObjectVisitor類來進行標記的,如下所示:

class MarkCompactMarkObjectVisitor {
 public:
  explicit MarkCompactMarkObjectVisitor(MarkCompact* collector) : collector_(collector) {
  }

  void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const ALWAYS_INLINE
      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
    // Object was already verified when we scanned it.
    collector_->MarkObject(obj->GetFieldObject<mirror::Object, kVerifyNone>(offset));
  }

  void operator()(mirror::Class* klass, mirror::Reference* ref) const
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
    collector_->DelayReferenceReferent(klass, ref);
  }

 private:
  MarkCompact* const collector_;
};
       這個類定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompactMarkObjectVisitor類有兩個版本的符號過載成員函式()。其中,上面一個用來處理那些指向了普通物件的成員變數,而下面一個用來處理指向了引用物件的成員變數。其中,普通物件通過前面分析過的MarkCompact類的成員函式MarkObject進行標記,而引用物件則通過MarkCompact類的成員函式DelayReferenceReferent延遲進行處理。注意,從前面分析的MarkCompact類的成員函式MarkingPhase可以知道,上述的引用物件在遞迴標記可達物件結束之後,會通過MarkCompact類的成員函式ProcessReferences進行處理。

       這一步完成之後,Mark-Compact GC的標記階段就執行完成了。注意,與Semi-Space GC和Generational Semi-Space GC不一樣,Mark-Compact GC在標記階段並沒有物件Bump Pointer Space的存活物件進行移動,而是在接下來的回收階段再執行此操作。

       前面提到,Mark-Compact GC的回收階段是通過呼叫MarkCompact類的成員函式ReclaimPhase來執行的。因此,接下來我們就繼續分析MarkCompact類的成員函式ReclaimPhase的實現,如下所示:

void MarkCompact::ReclaimPhase() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
  // Reclaim unmarked objects.
  Sweep(false);
  // Swap the live and mark bitmaps for each space which we modified space. This is an
  // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound
  // bitmaps.
  SwapBitmaps();
  GetHeap()->UnBindBitmaps();  // Unbind the live and mark bitmaps.
  Compact();
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式ReclaimPhase首先呼叫成員函式Sweep回收Non Moving Space和Large Object Space的垃圾,接著再呼叫成員函式SwapBitmaps交換ART執行時的Live Bitmap和Mark Bitmap。由於在回收垃圾之後,Live Bitmap和Mark Bitmap就沒有什麼用了,因此這時候MarkCompact類的成員函式ReclaimPhase還會呼叫Heap類的成員函式UnBindBitmaps清零各個Space的Live Bitmap和Mark Bitmap,以便下次GC時可以直接使用。

      最後,MarkCompact類的成員函式ReclaimPhase呼叫成員函式Compact對Bump Pointer Space的存活物件進行移動壓縮,並且修改引用了這些被移動物件的引用,它的實現如下所示:

void MarkCompact::Compact() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  CalculateObjectForwardingAddresses();
  UpdateReferences();
  MoveObjects();
  // Space
  int64_t objects_freed = space_->GetObjectsAllocated() - live_objects_in_space_;
  int64_t bytes_freed = reinterpret_cast<int64_t>(space_->End()) -
      reinterpret_cast<int64_t>(bump_pointer_);
  t.NewTiming("RecordFree");
  space_->RecordFree(objects_freed, bytes_freed);
  RecordFree(ObjectBytePair(objects_freed, bytes_freed));
  space_->SetEnd(bump_pointer_);
  // Need to zero out the memory we freed. TODO: Use madvise for pages.
  memset(bump_pointer_, 0, bytes_freed);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式Compact完成了以下三個重要的操作之後,再統計Mark-Compact GC釋放的物件數和記憶體位元組數:

       1. 呼叫MarkCompact類的成員函式CalculateObjectForwardingAddresses計算每一個將要被移動的物件要被移動到的新地址。

       2. 呼叫MarkCompact類的成員函式UpdateReferences引用那些引用了被移動物件的引用,因為這時候被移動物件的新地址已經確定了。

       3. 呼叫MarkCompact類的成員函式MoveObjects將要被移動的物件移動到前面計算好的新地址中。

       為了更好地理解Mark-Compact GC,接下來我們繼續對上述提到的MarkCompact類的三個成員函式的實現進行分析。

       MarkCompact類的成員函式CalculateObjectForwardingAddresses的實現如下所示:

void MarkCompact::CalculateObjectForwardingAddresses() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  // The bump pointer in the space where the next forwarding address will be.
  bump_pointer_ = reinterpret_cast<byte*>(space_->Begin());
  // Visit all the marked objects in the bitmap.
  CalculateObjectForwardingAddressVisitor visitor(this);
  objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
                                               reinterpret_cast<uintptr_t>(space_->End()),
                                               visitor);
}

       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式CalculateObjectForwardingAddresses首先是獲得當前正在處理的Bump Pointer Space的起始地址,並且儲存在成員變數bump_pointer_中,接下來再通過CalculateObjectForwardingAddressVisitor類的操作符過載函式()來處理每一個在當前正在處理的Bump Pointer Space上分配的、並且仍然存活的物件。

       CalculateObjectForwardingAddressVisitor類的操作符過載函式()的實現如下所示:

class CalculateObjectForwardingAddressVisitor {
 public:
  explicit CalculateObjectForwardingAddressVisitor(MarkCompact* collector)
      : collector_(collector) {}
  void operator()(mirror::Object* obj) const EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_,
                                                                      Locks::heap_bitmap_lock_) {
    ......
    collector_->ForwardObject(obj);
  }

 private:
  MarkCompact* const collector_;
};
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       CalculateObjectForwardingAddressVisitor類的操作符過載函式()呼叫MarkCompact類的成員函式ForwardObject來計算物件obj移動後的新地址,它的實現如下所示:

void MarkCompact::ForwardObject(mirror::Object* obj) {
  const size_t alloc_size = RoundUp(obj->SizeOf(), space::BumpPointerSpace::kAlignment);
  LockWord lock_word = obj->GetLockWord(false);
  // If we have a non empty lock word, store it and restore it later.
  if (lock_word.GetValue() != LockWord().GetValue()) {
    // Set the bit in the bitmap so that we know to restore it later.
    objects_with_lockword_->Set(obj);
    lock_words_to_restore_.push_back(lock_word);
  }
  obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(bump_pointer_)),
                   false);
  bump_pointer_ += alloc_size;
  ++live_objects_in_space_;
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式ForwardObject首先是獲得物件obj的大小alloc_size,接著再判斷它的LockWord是否不等於空。如果不等於空的話,就將其在MarkCompact類的成員變數objects_with_lockword_描述的一個ContinuousSpaceBitmap的對應位設定為1,並且將該LockWord儲存在MarkCompact類的成員變數lock_words_to_restore_描述的一個佇列中。

       從前面ART執行時Compacting GC簡要介紹和學習計劃一文可以知道,一個物件的LockWord描述了該物件的狀態資訊,例如鎖狀態、Hash值以及Forwarding Address等。由於接下來我們要將物件obj移動後的新地址記錄在它的LockWord中,也就是要借它的LockWord來用,因此當物件obj原來的LockWord不等於空的情況下,就需要先將它儲存起來,以便物件obj移動完成之後,可以恢復原來的LockWord。

       在Mark-Compact GC中,下一個物件要移動至的地址永遠記錄在MarkCompact類的成員變數bump_pointer_中,因此通過MarkCompact類的成員變數bump_pointer_就可以得到物件obj要移動至的地址。由於物件obj使用了當前MarkCompact類的成員變數bump_pointer_的值,因此我們需要更新它的值,即需要加上物件obj的大小,這樣就可以使得它永遠指向下一個要移動的物件的新地址。

       回到MarkCompact類的成員函式CalculateObjectForwardingAddresses中,當它執行完成之後,在當前正在處理的Bump Pointer Space上分配的、並且仍然活著的物件的新地址就計算完畢,即都已經儲存在它們自己的LockWord中,接下來就可以呼叫MarkCompact類的成員函式UpdateReferences來更新其它Space對當前正在處理的Bump Pointer Space的引用了。

       MarkCompact類的成員函式UpdateReferences的實現如下所示:

void MarkCompact::UpdateReferences() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  Runtime* runtime = Runtime::Current();
  // Update roots.
  runtime->VisitRoots(UpdateRootCallback, this);
  // Update object references in mod union tables and spaces.
  for (const auto& space : heap_->GetContinuousSpaces()) {
    // If the space is immune then we need to mark the references to other spaces.
    accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
    if (table != nullptr) {
      // TODO: Improve naming.
      TimingLogger::ScopedTiming t(
          space->IsZygoteSpace() ? "UpdateZygoteModUnionTableReferences" :
                                   "UpdateImageModUnionTableReferences",
                                   GetTimings());
      table->UpdateAndMarkReferences(&UpdateHeapReferenceCallback, this);
    } else {
      // No mod union table, so we need to scan the space using bitmap visit.
      // Scan the space using bitmap visit.
      accounting::ContinuousSpaceBitmap* bitmap = space->GetLiveBitmap();
      if (bitmap != nullptr) {
        UpdateObjectReferencesVisitor visitor(this);
        bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
                                 reinterpret_cast<uintptr_t>(space->End()),
                                 visitor);
      }
    }
  }
  CHECK(!kMovingClasses)
      << "Didn't update large object classes since they are assumed to not move.";
  // Update the system weaks, these should already have been swept.
  runtime->SweepSystemWeaks(&MarkedForwardingAddressCallback, this);
  // Update the objects in the bump pointer space last, these objects don't have a bitmap.
  UpdateObjectReferencesVisitor visitor(this);
  objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
                                               reinterpret_cast<uintptr_t>(space_->End()),
                                               visitor);
  // Update the reference processor cleared list.
  heap_->GetReferenceProcessor()->UpdateRoots(&MarkedForwardingAddressCallback, this);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       在分析MarkCompact類的成員函式UpdateReferences的實現之前,我們首先要明確有哪些Space的物件引用了當前正在處理的Bump Pointer Space的物件。從前面ART執行時Compacting GC堆建立過程分析一文可以知道,Mark-Compact GC使用的Space有Image Space、Zygote Space、Non-Moving Space、Bump Pointer Space和Large Object Space。因此,在這些Space上分配的物件都有可能對當前正在處理的Bump Pointer Space的物件產生引用。此外,還有三類物件可能會對當前正在處理的Bump Pointer Space產生引用,那就是根集物件、ART執行時內部分配的物件以及那些特殊處理的引用物件。

       對於根集物件,我們只需要呼叫Runtime類的成員函式VisitRoots重新遍歷一下它們,並且對於每一個根集物件,都呼叫MarkCompact類的靜態成員函式UpdateRootCallback來修改它們對當前正在處理的Bump Pointer Space的引用,如下所示:

void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
                                     RootType /*root_type*/) {
  mirror::Object* obj = *root;
  mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
  if (obj != new_obj) {
    *root = new_obj;
    DCHECK(new_obj != nullptr);
  }
}
      這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

      引數root描述的就是在當前呼叫棧上的、引用了根集物件的一個Slot。MarkCompact類的靜態成員函式UpdateRootCallback首先是呼叫成員函式GetMarkedForwardAddress檢查被引用的根集物件是否設定有Forward Address。如果設定有的話,就說明對應的呼叫棧Slot引用了當前正在處理的Bump Pointer Space的物件,因此這時候就需要修改該呼叫棧Slot的值,使得它引用的是移動後的根集物件。

       MarkCompact類的成員函式GetMarkedForwardAddress的實現如下所示:

inline mirror::Object* MarkCompact::GetMarkedForwardAddress(mirror::Object* obj) const {
  ......
  if (objects_before_forwarding_->HasAddress(obj)) {
    ......
    mirror::Object* ret =
        reinterpret_cast<mirror::Object*>(obj->GetLockWord(false).ForwardingAddress());
    ......
    return ret;
  }
  ......
  return obj;
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       如果物件obj位於MarkCompact類的成員變數objects_before_forwarding_描述的一個ContinuousSpaceBitmap中,那麼就說明它是一個位於當前正在處理的Bump Pointer Space的物件。又由於物件obj是一個根集物件,也就是它肯定是活著的,因此就可以從它的LockWord中獲取到它的Forwarding Address,也就是它移動後的地址。這個地址是在前面分析的MarkCompact類的成員函式ForwardObject計算並且設定的。

       回到MarkCompact類的成員函式UpdateReferences中,更新完成根集物件對正在處理的Bump Pointer Space的引用之後,接下來繼續更新Image Space、Zygote Space和Non Moving Space對正在處理的Bump Pointer Space的引用。這三個Space均是儲存在ART執行時堆的Continuous Space列表中。同時,儲存在這個Continuous Space列表中的Space還包含當前正在處理的Bump Pointer Space,但是由於這個Bump Pointer Space既沒有關聯有Mod Union Table,也沒有關聯有Live Bitmap,因此,中間的for迴圈只是用來更新Image Space、Zygote Space和Non Moving Space對正在處理的Bump Pointer Space的引用。

       對於Image Space和Zygote Space,它們都關聯有Mod Union Table,因此,我們就可以通過呼叫這個Mod Union Table的成員函式UpdateAndMarkReferences來遍歷上次GC以來發生過修改的物件。並且對於每一個這樣的物件,都呼叫MarkCompact類的靜態成員函式UpdateHeapReferenceCallback更新它們對當前正在處理的Bump Pointer Space的引用。

       MarkCompact類的靜態成員函式UpdateHeapReferenceCallback實現如下所示:

void MarkCompact::UpdateHeapReferenceCallback(mirror::HeapReference<mirror::Object>* reference,
                                              void* arg) {
  reinterpret_cast<MarkCompact*>(arg)->UpdateHeapReference(reference);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的靜態成員函式UpdateHeapReferenceCallback通過呼叫另外一個成員函式UpdateHeapReference來更新引數reference描述的位置(位於Image Space和Zygote Space上的某個物件的某個成員變數)對當前正在處理的Bump Pointer Space的引用,如下所示:

inline void MarkCompact::UpdateHeapReference(mirror::HeapReference<mirror::Object>* reference) {
  mirror::Object* obj = reference->AsMirrorPtr();
  if (obj != nullptr) {
    mirror::Object* new_obj = GetMarkedForwardAddress(obj);
    if (obj != new_obj) {
      DCHECK(new_obj != nullptr);
      reference->Assign(new_obj);
    }
  }
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       MarkCompact類的成員函式UpdateHeapReference與前面分析的MarkCompact類的成員函式UpdateRootCallback的實現邏輯基本上是一樣的,都是先通過呼叫MarkCompact類的成員函式GetMarkedForwardAddress判斷引數reference描述的位置引用的物件是否設定有Forwarding Address。如果設定有的話,就需要修改該位置的值。

       回到MarkCompact類的成員函式UpdateReferences中,對於Non Moving Space,它沒有關聯有Mod Union Table,但是它關聯有Live Bitmap。因此,通過這個Live Bitmap,也可以逐個地檢查當前Non Moving Space的存活物件是否引用了當前正在處理的Bump Pointer Space的物件。如果有引用,那麼就通過UpdateObjectReferencesVisitor類的操作符過載函式()對它們的引用位置進行修改。

       UpdateObjectReferencesVisitor類的操作符過載函式()的實現如下所示:

class UpdateObjectReferencesVisitor {
 public:
  explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {
  }
  void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
          EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
    collector_->UpdateObjectReferences(obj);
  }

 private:
  MarkCompact* const collector_;
};
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       從這裡就可以看到,在Non Moving Space上分配的物件,是通過呼叫MarkCompact類的成員函式UpdateObjectReferences來修改它們對當前正在處理的Bump Pointer Space的引用的。

       MarkCompact類的成員函式UpdateObjectReferences的實現如下所示:

void MarkCompact::UpdateObjectReferences(mirror::Object* obj) {
  UpdateReferenceVisitor visitor(this);
  obj->VisitReferences<kMovingClasses>(visitor, visitor);
}
      這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

      MarkCompact類的成員函式UpdateObjectReferences通過呼叫Object類的成員函式VisitReferences來遍歷物件obj每一個型別為引用的成員變數,並且通過UpdateReferenceVisitor類的操作符過載函式()來更新它們對當前正在處理的Bump Pointer Space的引用。

      UpdateReferenceVisitor類的操作符過載函式()有兩個版本的實現,如下所示:

class UpdateReferenceVisitor {
 public:
  explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {
  }

  void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
      ALWAYS_INLINE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
    collector_->UpdateHeapReference(obj->GetFieldObjectReferenceAddr<kVerifyNone>(offset));
  }

  void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const
      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
    collector_->UpdateHeapReference(
        ref->GetFieldObjectReferenceAddr<kVerifyNone>(mirror::Reference::ReferentOffset()));
  }

 private:
  MarkCompact* const collector_;
};
       這兩個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       如果物件obj有一個引用型別的成員變數,那麼這個成員變數要麼是一個普通的引用,即是一個強引用,要麼是一個弱引用,即Soft Reference、Weak Reference、Phantom Rerference或者Finalizer Reference。對於前者,通過上面版本的操作符過載函式()進行處理;而對於後者,通過下面版本的操作符過載函式()進行處理。這兩者唯一的區別就是獲得被引用物件的方式不同。一旦獲得了被引用物件,就可以呼叫前面分析過的MarkCompact類的成員函式UpdateHeapReference來更新對應的成員變數的值了。

       回到MarkCompact類的成員函式UpdateReferences中,這時候Image Space、Zygote Space和Non Moving Space的物件對當前正在處理的Bump Pointer Space的物件的引用就更新完畢,接下來需要繼續對Large Object Space和Bump Pointer Space進行同樣的處理。

       首先看對Large Object Space的處理。從前面ART執行時Compacting GC為新建立物件分配記憶體的過程分析一文可以知道,在Large Object Space上分配的物件都是屬於原子類陣列物件。原子類陣列物件只有一個引用型別的成員變數,它引用的是一個Class物件,該Class物件描述原子類陣列物件的型別。除此之外,原子類陣列物件就沒有其它引用型別的成員變量了。

        ART執行時定義了一個常量kMovingClasses,當ART執行時支援Mark-Compact GC時,它的值就等於false,如下所示:

// True if we allow moving classes.
static constexpr bool kMovingClasses = !kMarkCompactSupport;
        這個常量定義在檔案art/runtime/globals.h中。

        又當常量kMovingClasses的值等於false時,ART執行時在分配Class物件時,都會在Non Moving Space進行分配,如下所示:

mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class,
                                       uint32_t class_size) {
  ......
  gc::Heap* heap = Runtime::Current()->GetHeap();
  mirror::Class::InitializeClassVisitor visitor(class_size);
  mirror::Object* k = kMovingClasses ?
      heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) :
      heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor);
  ......
  return k->AsClass();
}
       這個函式定義在檔案art/runtime/class_linker.cc。

       這意味著在Large Object Space上分配的物件,不可能會引用在Bump Pointer Space分配的物件,因此MarkCompact類的成員函式UpdateReferences就不需要對Large Object Space作任何的處理。

       我們再來看MarkCompact類的成員函式UpdateReferences對Bump Pointer Space的處理。這個Bump Pointer Space實際上就是正在處理的Bump Pointer Space,在上面分配的物件是可能發生相互引用的情況的,因此MarkCompact類的成員函式UpdateReferences就需要對它進行處理。

       雖然Bump Pointer Space既沒有關聯有Mod Union Table,也沒有關聯有Live Bitmap,不過MarkCompact類的成員變數objects_before_forwarding_描述的ContinuousSpaceBitmap起到的作用與Live Bitmap是一樣的,因此MarkCompact類的成員函式UpdateReferences就可以像處理Non Moving Space的方式一樣處理Bump Pointer Space。

       現在,就剩下ART執行時內部分配的物件以及那些特殊處理的引用物件沒有處理了。ART執行時內部分配的物件指的就是那些常量字串池的字串物件、Monitor物件和在JNI建立的全域性弱引用物件等。對於這一類存活的物件,通過呼叫Runtime類的成員函式SweepSystemWeaks進行遍歷。Runtime類的成員函式SweepSystemWeaks在遍歷這些物件的過程中,會呼叫MarkCompact類的靜態成員函式MarkedForwardingAddressCallback來判斷它們是否引用了當前正在處理的Bump Pointer Space的物件。如果引用了的話,那麼就會更新相應的位置的引用值。

       MarkCompact類的靜態成員函式MarkedForwardingAddressCallback的實現如下所示:

mirror::Object* MarkCompact::MarkedForwardingAddressCallback(mirror::Object* obj, void* arg) {
  return reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       從這裡就可以看到,MarkCompact類的靜態成員函式MarkedForwardingAddressCallback是呼叫前面分析過的MarkCompact類的成員函式GetMarkedForwardAddress來決斷物件obj是否設定有Forwarding Address的。如果設定有Forwarding Address,那麼就說明物件obj是位於當前正在處理的Bump Pointer Space中的,因此就會把該Forwarding Address返回給呼叫者,以便呼叫者更新對應位置的引用值。

       最後,就剩下引用物件沒有處理沒有處理了。引用物件,即Soft Reference、Weak Reference、Phantom Rerference和Finalizer Reference這些型別的物件,它們都是通過ReferenceProcessor類來進行處理的。ReferenceProcessor類內部維護有了一個目標物件已經被回收了的引用物件列表。這些引用物件有可能是在當前正在處理的Bump Pointer Space中分配的,因此就需要對該列表進行更新。這是通過呼叫ReferenceProcessor類的成員函式UpdateRoots進行處理的,並且也是通過MarkCompact類的靜態成員函式MarkedForwardingAddressCallback來獲得是在當前正在處理的Bump Pointer Space中分配的物件的Forwarding Address的。

       這樣,當MarkCompact類的成員函式UpdateReferences執行完畢,所有引用了位於當前正在處理的Bump Pointer Space上的物件的位置均已得到了更新。回到MarkCompact類的成員函式Compact中,它接下來要做的一件事情就是呼叫另外一個成員函式MoveObjects對當前正在處理的Bump Pointer Space的物件進行移動,也就是將存活的物件按地址值從小到大的順序依次排列在Bump Pointer Space的前半部分,以便解決記憶體碎片問題。

       MarkCompact類的成員函式MoveObjects的實現如下所示:

void MarkCompact::MoveObjects() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  // Move the objects in the before forwarding bitmap.
  MoveObjectVisitor visitor(this);
  objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
                                               reinterpret_cast<uintptr_t>(space_->End()),
                                               visitor);
  CHECK(lock_words_to_restore_.empty());
}

       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       由於前面已經將當前正在處理的Bump Pointer Space上每一個存活物件的新地址都已經計算好了,因此現在只需要將它們從原地址拷貝過去即可。其中,當前正在處理的Bump Pointer Space上的存活物件可以通過MarkCompact類的成員變數objects_before_forwarding_指向的一個ContinuousSpaceBitmap物件獲得,而這些存活物件的移動是通過MoveObjectVisitor類的操作符過載函式()來實現的,如下所示:

class MoveObjectVisitor {
 public:
  explicit MoveObjectVisitor(MarkCompact* collector) : collector_(collector) {
  }
  void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
          EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
      collector_->MoveObject(obj, obj->SizeOf());
  }

 private:
  MarkCompact* const collector_;
};
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       從這裡可以看到,MoveObjectVisitor類的操作符過載函式()是通過呼叫MarkCompact類的成員函式MoveObject來移動引數obj描述的物件的,後者的實現如下所示:

void MarkCompact::MoveObject(mirror::Object* obj, size_t len) {
  // Look at the forwarding address stored in the lock word to know where to copy.
  DCHECK(space_->HasAddress(obj)) << obj;
  uintptr_t dest_addr = obj->GetLockWord(false).ForwardingAddress();
  mirror::Object* dest_obj = reinterpret_cast<mirror::Object*>(dest_addr);
  DCHECK(space_->HasAddress(dest_obj)) << dest_obj;
  // Use memmove since there may be overlap.
  memmove(reinterpret_cast<void*>(dest_addr), reinterpret_cast<const void*>(obj), len);
  // Restore the saved lock word if needed.
  LockWord lock_word;
  if (UNLIKELY(objects_with_lockword_->Test(obj))) {
    lock_word = lock_words_to_restore_.front();
    lock_words_to_restore_.pop_front();
  }
  dest_obj->SetLockWord(lock_word, false);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       這裡首先需要明確,引數obj是位於正在處理的Bump Pointer Space的,因此,我們就可以通過它的LockWord獲得它的Forwarding Address,也就是它要移動至的新地址。有了這個新地址之後,就可以通過memmove將它的內容從舊地址拷貝到新地址了。

       由於物件obj的LockWord被借用了來儲存新地址,因此,當它被移動至新地址之後,需要恢復它原先的LockWord值。當然,並不是所有要移動的物件的LockWord值都需要恢復,因為有些物件的LockWord值本來就是空的。對於原來的LockWord值為空的物件,只需要將移動後得到的物件的LockWord值也設定為空即可。而對於原來的LockWord值不空的物件,可以通過MarkCompact類的成員變數lock_words_to_restore_指向的一個佇列獲得。並且判斷物件obj原來的LockWord值不為空可以通過MarkCompact類的成員變數objects_with_lockword_指向的一個ContinuousSpaceBitmap物件判斷。

      最後一點還需要注意的是,在計算正在處理的Bump Pointer Space上的存活物件的新物件時,是按照地址值從小到大的順序進行的。也就是說,對於原來LockWord值不為空的物件,它們原來的LockWord值也是按照物件地址從小到大的順序儲存在MarkCompact類的成員變數lock_words_to_restore_指向的一個佇列中的。現在執行移動物件的操作時,也是按照物件地址從小到大的順序進行的,因此就可以依次從上述佇列中準確地獲得物件原先的LockWord值。

      這樣,Mark-Compact GC的回收階段也執行完成了,最後剩下的結束階段是通過呼叫MarkCompact類的成員函式FinishPhase實現的,如下所示:

void MarkCompact::FinishPhase() {
  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
  space_ = nullptr;
  CHECK(mark_stack_->IsEmpty());
  mark_stack_->Reset();
  // Clear all of the spaces' mark bitmaps.
  WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
  heap_->ClearMarkedObjects();
  // Release our bitmaps.
  objects_before_forwarding_.reset(nullptr);
  objects_with_lockword_.reset(nullptr);
}
       這個函式定義在檔案art/runtime/gc/collector/mark_compact.cc中。

       Mark-Comapctt GC的結束階段主要是執行一些清理工作,包括:

       1. 將MarkCompact類的成員變數space_置空。這個成員變數用來指定當前要進壓縮處理的Bump Pointer Space。每次在執行Mark-Compact GC之前,會首先對它進行初始化。

       2. 重置ART執行時堆的Mark Stack。這個Mark Stack儲存在MarkCompact類的成員變數mark_stack_。在每次Mark-Compact GC的初始化階段,都會對該成員變數進行初始化。

       3. 呼叫Heap類的成員函式ClearMarkedObjects對ART執行時堆的各個Space的Mark Bitmap進行清零的操作,以便下次可以使用。

       4. 將MarkCompact類的成員變數objects_before_forwarding_和objects_with_lockword_置空。它們在每次Mark-Compact GC的標記階段會被重新建立和初始化,並且在回收階段進行使用。

       至此,Mark-Compact GC的執行過程就分析完成了。與前面ART執行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析一文分析的Semi-Space GC和Generational Semi-Space GC相比,它們的共同特點都是在Stop-the-word前提下進行的,並且都是通過移動物件來實現垃圾回收和記憶體壓縮的目的。不過前者的物件移動操作是就地進行的,而後兩者的物件移動操作是要通過一個額外的Space來進行的。當然,Mark-Compact GC的物件移動操作可以就地進行並不是免費的午餐,它要付出的代價在回收階段執行一個額外的新地址計算操作。這又是一個計算機世界以時間換空間或者說以空間換時間的例子。

       現在,我們就對ART執行時的Mark-Sweep GC和Compacting GC都做了分析,也知道了它們的優缺點。如果我們能夠揚長避短,那麼就可以既解決GC的效率問題,又能解決記憶體的碎片問題。在接下來的一篇文章中,我們就會重點分析ART執行時是如何最大限度地發揮Mark-Sweep GC和Compacting GC的長處的,敬請關注!更多的資訊也可以關注老羅的新浪微博:http://weibo.com/shengyangluo/home