文章目錄
  1. 1. 不会暂停的垃圾回收器,C4 The Continuously Concurrent Compacting Collector,by Zing JVM @azulsystems
    1. 1.1. 垃圾收集器相关
    2. 1.2. 简要翻译
      1. 1.2.0.1. Write Barrier
      2. 1.2.0.2. Write Barrier实现方式1:Remembered Set && 卡表(card table)
      3. 1.2.0.3. Write Barrier实现方式2:Store Buffer
      4. 1.2.0.4. mutator

不会暂停的垃圾回收器,C4 The Continuously Concurrent Compacting Collector,by Zing JVM @azulsystems

最近在看GC有很多不错的文章,顺便翻一下C4吧,似乎很神的样子

垃圾收集器相关

G1是Oracle下一代垃圾回收器,CMS的替代者一个不错的中文介绍

C4是Azulsystems的一篇论文,该公司提供了一个不会stop-the-world的zing JVM,知乎上大名鼎鼎的 @RednaxelaFX 就在这个公司。这是唯一找到的一篇C4中文介绍

隔行如隔山,这个C4的论文实在是看不懂,还好我找到了RednaxelaFX大人的blog。如果你也读不懂,我建议先先去看看

简要翻译

其中很多参考了 @RednaxelaFX的内容以及他的,blog,表示感谢:)

以及R大的一些讨论

http://hllvm.group.iteye.com/group/topic/44381#post-272188

http://hllvm.group.iteye.com/group/topic/21468

Write Barrier

这篇论文讲的非常清楚,讲了很多实现,说的也很清楚:一篇关于Write Barrier以及Store Buffer的论文

write barrier 是在实现部分垃圾收集(partial GC)时用于记录从非收集部分指向收集部分的指针的集合的结构。

分代式GC是一种部分垃圾收集的实现方式。当分两代时,通常把这两代叫做young gen和old gen;通常能单独收集的只是young gen。此时remembered set记录的就是从old gen指向young gen的跨代指针。

Write Barrier实现方式1:Remembered Set && 卡表(card table)

1
所以说 Remembered Set 和卡表应该都算是Write Barrier的一种实现方式,当然,卡表还算是Remembered Set的一个特例。
  • 粒度问题

所谓粒度问题,就是每个指向yong代的指针到底代表多大的一块空间。所以无论是remembered set还是card table,记录精度都有很大的选择余地:

  1. 字粒度:每个记录精确到一个机器字(word)。该字包含有跨代指针。
  2. 对象粒度:每个记录精确到一个对象。该对象里有字段含有跨代指针。
  3. card粒度:每个记录精确到一大块内存区域。该区域内有对象含有跨代指针。
  4. 还有其它可能性,任君想像

但一般而言,有一些隐含假设,当提到Remembered Set,一般指的是对象粒度,而卡表一般值一个内存块。

  • 实现数据结构
  1. 对于地址空间较大的情况,可以考虑直接使用指针,也就是一般意义上的Remembered Set
1
2
3
struct RememberedSet {
Object* data[MAX_REMEMBEREDSET_SIZE];
};

或者

1
2
3
4
5
typedef char* address;

struct RememberedSet {
address* data[MAX_REMEMBEREDSET_SIZE];
};
  1. Card Table 则是Remembered Set一种特殊实现,用每个bit隐式代表一块内存区域,所以会格外省空间
1
2
3
struct CardTable {
byte table[MAX_CARDTABLE_SIZE];
};
  • 简要实现方式猜测

这块目前还没看论文,我猜测应该是在新生代生成新对象的时候,查看是否有指向这些新生代的老年代对象,从而更改Remembered Set。

疑问1: 那么在做Full GC时,是不是也要从新清理一遍整个Remember Set呢?

Write Barrier实现方式2:Store Buffer

还有一个与remembered set相关的概念,叫做store buffer。由于其实现方式也被称为“sequential store buffer(SSB)”。
有些资料会把store buffer也看作remembered set的一种实现,但我喜欢把前者看作与后者相关/近似的概念,而不是“实现方式”。

例如最老的V8使用per-page remembered set,而比较新的版本使用store buffer。
(使用remembered set的V8,以最早的V8 0.1为例,每个“Page”有8KB,其中开头有248字节用于remembered set(RSet)。RSet里每个bit对应该Page里的一个word,所以这是word精度的。

而使用store buffer的V8也是word精度的。)

两者的相似之处在于它们都记录跨区域的指针。
而最重要的区别是:remembered set是一个集合(set),所以不包含重复;store buffer则通常允许包含重复。

Store buffer的write-barrier比要去重复的remembered set的writer-barrier要简单和高效,但由于其允许重复,前者在部分收集(例如young GC)时的开销会比后者大。

一个折衷的办法是在mutator的write-barrier还是允许重复,然后周期性增量式或在另一个线程并发的对store buffer去重。这样到实际执行部分收集时重复条目的数量可以大幅减少,提高GC的效率。V8的store buffer就是这样做的。

这种还需要对数据做后续处理的write-barrier也叫做logging write-barrier。

从F大的论述中可以才

mutator

感觉是内存分配器?

文章目錄
  1. 1. 不会暂停的垃圾回收器,C4 The Continuously Concurrent Compacting Collector,by Zing JVM @azulsystems
    1. 1.1. 垃圾收集器相关
    2. 1.2. 简要翻译
      1. 1.2.0.1. Write Barrier
      2. 1.2.0.2. Write Barrier实现方式1:Remembered Set && 卡表(card table)
      3. 1.2.0.3. Write Barrier实现方式2:Store Buffer
      4. 1.2.0.4. mutator