ART GC ModUnionTable 實現及使用

上一篇 MarkSweep MarkingPhase的學習中,其實沒有完全搞明白ModUnionTable到底是如何工作的,只是根據代碼和註釋大概知道其意思。

本篇來回顧一下 ART 中 ModUnionTable數據結構,以及搞明白它是如何使用的。

1.ModUnionTable類

// The mod-union table is the union of modified cards. It is used to allow the card table to be
// cleared between GC phases, reducing the number of dirty cards that need to be scanned.
class ModUnionTable {
  typedef std::set<uint8_t*, std::less<uint8_t*>,
                   TrackingAllocator<uint8_t*, kAllocatorTagModUnionCardSet>> CardSet;
  typedef MemoryRangeBitmap<CardTable::kCardSize> CardBitmap;

  const std::string name_;
  Heap* const heap_;
  space::ContinuousSpace* const space_;
}
其實我們學習代碼的時候,看註釋很也重要的,能夠幫助我們更快的理解作者寫這段代碼的想法。
這裏就指明瞭 ModUnionTable 是個什麼東西,以及它的設計目的:
  1. 一個 ModUnionTable 就是記錄這 modified cards的集合,這裏的 card與 card table中的card是一個意思;所以 ModUionTable的全稱應該是:The Union of modifed cards。
  2. 使用 ModUnionTable,使得可以在 GC Mark階段去 clean card table,並 scan 這些 card對應的數據,可以減少一部分在GC Pause階段的工作
另外需要關注的兩個 typedef 類型:
  1. CardSet:它是一個 std::set,將會在後面 ImageSpace對應的 ModUnionTable中使用
  2. CardBitmap:它是一個 MemoryRangeBitmap<kAlignMent>,實際上是 bitmap的一個變體,bitmap是一個 bit 表示一個Object(至少8byte)對象的存活狀態;而MemoryRangeBItmap的一個 bit 表示 kAlignMent 個字節的存活情況。在這裏,alignment是 CardTable::kCardSize,說明了其單位與 CardTable的 一個 card是一樣的。即,它的一個bit代表一個 card
接下來需要關注它的兩個比較重要的成員函數:
  // Process cards for a memory range of a space. This doesn't immediately update the mod-union
  // table, as updating the mod-union table may have an associated cost, such as determining
  // references to track.
  virtual void ProcessCards() = 0;

  // Update the mod-union table using data stored by ProcessCards. There may be multiple
  // ProcessCards before a call to update, for example, back-to-back sticky GCs. Also mark
  // references to other spaces which are stored in the mod-union table.
  virtual void UpdateAndMarkReferences(MarkObjectVisitor* visitor) = 0;
  1. ProcessCards() :處理一個 space 的指定 memory range對應的 card,一般是處理整個space對應的 card;它是一個純虛函數,可以知道後面 ImageSpace 和ZygoteSpace 的 ModUionTable,都是它的子類了
  2. UpdateAndMarkReferences():根據 ProcessCards()準備的數據,進行 MarkReference,在執行這個函數之前,可以執行多次 ProcessCards()準備數據;它也是一個純虛函數,需要子類實現
總結:ModUnionTable 設計的目的是爲了處理(Mark)當前 space 中被修改過的 Cards中的每個對象的引用;其數據的(哪些Card被修改過)收集,是在 ProcessCards()函數中獲取的;當前 space中對象的引用的處理是在 UpdateAndMarkReferences() 函數實現的。

2.ModUnionTableCardCache 類

// Card caching implementation. Keeps track of which cards we cleared and only this information.
class ModUnionTableCardCache : public ModUnionTable {

  // Clear and store cards for a space.
  virtual void ProcessCards() OVERRIDE;

  // Mark all references to the alloc space(s).
  virtual void UpdateAndMarkReferences(MarkObjectVisitor* visitor);

  // Cleared card bitmap, used to update the mod-union table.
  std::unique_ptr<CardBitmap> card_bitmap_;
}

先說明一下 card_bitmap_ 成員,它會在 ModUnionTableCardCache 構造函數中被初始化,其代表整個 space的全部範圍(the limit, not the current end).
ModUnionTableCardCache 這個類使用來描述 ZygoteSpace 的 mod union table的。這個類的作用是 "keep track of which cards we cleared",也就是說,當我們把一個card 從dirty(0x70)狀態clear爲非 dirty(0x6f)狀態時,我們會使用 card_bitmap_記錄下來這個card(就是把這個card對應在 card_bitmap_上的 bit 置爲 1)。
在這個類裏,兩個關鍵函數的實現,都已經比較明確了:
  1. ProcessCards():清空一個 space 對應的 card table,就是修改整個heap的 card table,card table中對應這個space的所有 byte,只要有 dirty(0x70)的,都會被清除(修改爲0x6f),如果原始值是其他的值(0x6f,0x0),則修改爲 0x0(clean)。對於是 0x70 -> 0x6f 改動的card,將會把這個 card 在 card_bitmap_ 中對應的 bit 設置爲1,用以記錄
  2. UpdateAndMarkReferences():其功能是 mark all references to the alloc space。對 card_bitmap_中被設置了 1的bit對應的 card(128byte)中的所有 object 依次進行 Visit,如果這個 object 的成員對象不屬於當前space(ZygoteSpace),也不屬於 ImmuneSpace(ImageSpace),那麼這個成員對象就是在 AllocSpace中,就需要對這個成員對象進行Mark操作,這樣就達到了通過 ZygoteSpace 的 mod union table 來標記ZygoteSpace中的對象引用的 Alloc Space中的對象的目的了。(疑問:獲取 ImmueSpace時,是這麼獲取的:heap_->GetBootImageSpaces()[0],那麼其他的ImageSpace 不管了 ? 其中還有個 ref update操作,還沒看懂)
第一個函數 ProcessCards()的實現比較簡單,處理整個Space中的所有dirty cards,並設置到 card_bitmap_ 即可:
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);
}
第二個函數 UpdateAndMarkReference(),實現較爲複雜,大概涉及了下面這麼幾個函數和 Visitor,有興趣的同學可以看下其實現:
  1. void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor)
  2. card_bitmap_->VisitSetBits
  3. CardBitVisitor
  4. ContinuousSpaceBitmap->VisitMarkedRange(start, start+128)
  5. ModUnionScanImageRootVisitor
  6. object->VisitReferences()
  7. ModUnionUpdateObjectReferencesVisitor->MarkReference
  8. MarkObjectVisitor->MarkObject
Zygote Space 的 mod union table 圖解:

圖解:
  1. card_bitmap_ 中標記 ProcessCards() / ClearCards() 時狀態爲 dirty 的 card
  2. 依次 Visit dirty card 範圍內的,且在 space 的bitmap 中處於 live 狀態的 object,比如 obj1,obj2,obj4 符合條件
  3. obj1 中的 ref1 和 ref2 地址都是在 Alloc Space 範圍內,所以需要標記 這兩個 ref
  4. obj4 的 ref3 在 ImageSpace ( immune_space_)範圍內,ref4 在 ZygoteSpace 內,都不需要進行標記
  5. 所以 ZygoteSpace mod union table 僅僅是處理從 zygote space 到 alloc space 的引用
總結:ModUnionTableCardCache 這個類,就是 ZygoteSpace 的 mod union table,當一個對象在 ZygoteSpace內,切在 GC 階段發現這個對象所在 card 被修改過(dirty,即 card 中有對象被修改了引用,且不是修改成null),而這個對象的一些引用不在 ZygoteSpace,也不在 ImageSpace,則應該就是在 AllocSpace,會對其進行標記。

3.ModUnionTableReferenceCache類

// Reference caching implementation. Caches references pointing to alloc space(s) for each card.
class ModUnionTableReferenceCache : public ModUnionTable {
  // Clear and store cards for a space.
  void ProcessCards() OVERRIDE;

  // Update table based on cleared cards and mark all references to the other spaces.
  void UpdateAndMarkReferences(MarkObjectVisitor* visitor)

  // Function that tells whether or not to add a reference to the table.
  virtual bool ShouldAddReference(const mirror::Object* ref) const = 0;

  // Cleared card array, used to update the mod-union table.
  ModUnionTable::CardSet cleared_cards_;

  // Maps from dirty cards to their corresponding alloc space references.
  AllocationTrackingSafeMap<const uint8_t*, std::vector<mirror::HeapReference<mirror::Object>*>,
                            kAllocatorTagModUnionReferenceArray> references_;
}

這個類的作用就是:“Caches references pointing to alloc sapces for each card”. 簡單來說就是保存我們想要處理的 Reference,至於什麼樣的 Reference 我們處理,這個函數 ShouldAddReference()就是用來判斷這個情況的,純虛函數,所以應該還是有子類來實現這個功能。
其兩個成員: 
  1. cleared_cards_:當把一個 dirty card clear後,我們會把這個 card 記錄下來;在後面UpdateAndMarkReference時根據這個數據來做標記;
  2. references_:就是記錄dirty cards中指向其他 space 的 References;
ProcessCard()函數在clean dirtycard時,會把card記錄到 cleared_cards_中,這個與 ModUnionTableCardCache中的僅僅使用card bitamp記錄不同;

4.ModUnionTableToZygoteAllocspace類

// A mod-union table to record image references to the Zygote and alloc space.
class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache {
  bool ShouldAddReference(const mirror::Object* ref) const OVERRIDE ALWAYS_INLINE {
    return !space_->HasAddress(ref);
  }
}
ModUnionTableToZygoteAllocspace 纔是實際上 iamge space的 ModUnionTbale。而它的主要功能實現也還是在 ModUnionTableReferenceCache 中實現,這裏只是實現了 ShouldAddReference()函數,意思說,當前 reference不在ImageSpace中,則需要把這個 reference 添加到當前 modunion table的 reference_ 的map中。

總結:ImageSpace的 ModUnionTable的使用步驟大致如下:
  1. ProcessCards()時,把 dirty card 保存在 cleared_cards_中
  2. 在UpdateAndMarkReferences() 函數中,按照 card順序,找到card對應space(ImageSpace)的 live_bitmap_,然後在一定範圍內(card start到 card end範圍),Visit live_bitmap_中被標記的對象,在Visit過程中,如果發現該對象的成員Reference地址不在ImageSpace中,則把該Reference地址記錄到 references_這個cache中,最後會標記這個 cache中的 reference 以及其可達對象。如果在這個過程中,發現ImageSpace的對象的引用是 GCRoot,且該 GCRoot不在ImageSpace中,那麼還需要把該ImageSpce對象所在的 card記錄到 cleared_cards_中(一般發生在 classloader的情況),以便下次mark;
ImageSpace 的 mod union table 圖解:

圖解:
  1. cleared_cards_ 中 card1 和 card4 表示 ProcessCards() / ClearCards() 時處於 dirty 的 card,即 card_table_ 中灰色背景,值爲 0x70 的card;
  2. Image Space 中綠色背景的 obj1,obj2 是 card1 範圍內的 object,且在 space live_bitmap中被標記爲存活,obj4 是 card4 對應範圍內的 obj,且在 heap live_bitmap_ 中被標記爲存活;
  3. obj1 的引用 ref2 指向 ImageSpace,所以不用記錄到  references_ map中; obj1 的引用 ref1 指向 Zygote space,obj4 的引用 ref3,ref4指向 Alloc Space,所以它們要被添加到該 mod union table 的數據集合 references_ map 中,以便後續訪問標記;
  4. 另外,假若途中 card1 中存在 GCRoot,且地址範圍在 Zygote Space 或者 Alloc Space,則記錄下來 card1,等到標記 references_ 中的 ref 完成,清空完 cleard_cards_ 之後,會再次把 card1 添加到 cleard_cards_ 中;
  5. 所以 ImageSpace 的 mod union table 是處理從 Image space 到 zygote space 和到 alloc space 的引用;
總結:Image Space 的 mod union table 主要通過 cleared_card_ 和 references_ 這兩個成員來工作;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章