关于 JVM 回收中 跨代之间的引用 card marking, dirty card, write barrier

JVM GC 回收对象的时候, 都是先找出 Root 对象, 然后按照引用关系作出引用关系图, 得到live 对象, 剩下的都是 dead 对象, 继而回收掉这些 dead 对象.

分代回收的时候, 先回收年轻代, 为什么回收年轻代那么快? 仅仅因为年轻代比较小吗? 回收年轻代的时候, 要扫描所有的老年代对象吗? 这里就涉及到 dirty card.
年轻代回收的时候, 首先找出 root 对象, 然后对于从老年代到年轻代的引用, 可以有下面几种方式来发现老年代到年轻代的引用:

  1. 从 root 对象开始找到从老年代到年轻代的引用关系 - 比较耗时, 相当于整个 heap 都做引用关系图;
  2. 忽略 root 对象, 扫描所有老年代对象, 然后找到从老年代到年轻代的引用 - 也挺多的.

openJDK 采用的是上面第二种方式的一个变种. 对老年代 heap 设立一个 dirty card 表. 例如对老年代每256K 映射到 dirty card 中的一个 bit, 如果老年代有对年轻代的引用的时候(老年代对象字段值的修改), 把这个 bit 修改代表 dirty.

一个过程如下:
假如刚做完一次 full GC, 年轻代都是空的, 那么如下:

  1. 如果有新分配对象, 它一定在年轻代, 如果有老年代对象指向这个新对象(字段赋值), 那么这个老年代对象所对应内存的对应
    dirty card bit 被修改为 dirty;
  2. 等到 minor GC 发生的时候, GC 把老年代 dirty card 里面 dirty bit 对应的内存部分的老年代对象和加入到 root 里面, 就可以了. 这样避免了扫描全部. 一旦年轻代 mark 阶段结束, 整个 dirty card 全部恢复为空;
  3. 一旦年轻代开始从 eden 和 from survival copy 到 to survival 或者 promote 部分对象到老年代, 那么 dirty card 的 dirty bit 就开始工作了. 有指针引用的就会变成 dirty bit;

同时年轻代比较小, 所以一次能容纳的对象比较少, 所以 dirty card 里面 dirty bit 不会太多.

随便摘要, 更详细信息可以从这里看:
https://www.ibm.com/developerworks/library/j-jtp11253/http://blog.ragozin.info/2011/06/understanding-gc-pauses-in-jvm-hotspots.html

标签: none

添加新评论