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_ 这两个成员来工作;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值