☕[JVM技术指南](4)垃圾回收子系统(Garbage Collection System)之G1垃圾收集器SATB

Java
312
0
0
2022-04-22

对象漏标

垃圾回收的并发标记阶段,gc线程和应用线程是并发执行的,所以一个对象被标记之后,应用线程可能篡改对象的引用关系,从而造成对象的漏标、误标。

  • 误标没什么关系,顶多造成浮动垃圾,在下次gc还是可以回收的。

  • 漏标的后果是致命的,把本应该存活的对象给回收了,从而影响的程序的正确性。

为了解决在并发标记过程中,存活对象漏标的情况,GC HandBook把对象分成三种颜色:

  1. 黑色:自身以及可达对象都已经被标记
  2. 灰色:自身被标记,可达对象还未标记
  3. 白色:还未被标记

所以,漏标的情况只会发生在白色对象中,且满足以下任意一个条件:

  1. 并发标记时,应用线程给一个黑色对象的引用类型字段赋值了该白色对象
  2. 并发标记时,应用线程删除所有灰色对象到该白色对象的引用

对于第一种情况,利用post-write barrier,记录所有新增的引用关系,然后根据这些引用关系为根重新扫描一遍

对于第二种情况,利用pre-write barrier,将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根重新扫描一遍

SATB

SATB全称【snapshot-at-the-beginning】,由Taiichi Yuasa为增量式标记清除垃圾收集器开发的一个算法,主要应用于垃圾收集的并发标记阶段,解决了CMS垃圾收集器重新标记阶段长时间STW的潜在风险。

Region包含了5个指针,分别是bottom、previous TAMS、next TAMS、top和end

☕[JVM技术指南](4)垃圾回收子系统(Garbage Collection System)之G1垃圾收集器SATB

其中previous TAMS、next TAMS是前后两次发生并发标记时的位置,全称top-at-mark-start

  1. 假设第n轮并发标记开始,将该Region当前的top指针赋值给next TAMS,在并发标记标记期间,分配的对象都在[next TAMS, top]之间,SATB能够确保这部分的对象都会被标记,默认都是存活的

  2. 当并发标记结束时,将next TAMS所在的地址赋值给previous TAMS,SATB给 [bottom, previous TAMS] 之间的对象创建一个快照Bitmap,所有垃圾对象能通过快照被识别出来

  3. 第n+1轮并发标记开始,过程和第n轮一样

SATB保证了在并发标记过程中新分配对象不会漏标

  • 但如果在TAMS之前有一个白色对象W,被一个灰色对象G引用,在并发标记扫描到这个字段之前被赋值为null,切断了对象W和对象G之间的引用关系,对象W就有可能漏标,这就是白色对象被漏标的第二种情况?

G1中如何解决?

在引用关系被修改之前,插入一层 pre-write barrier

☕[JVM技术指南](4)垃圾回收子系统(Garbage Collection System)之G1垃圾收集器SATB

pre-write barrier最终执行逻辑:

☕[JVM技术指南](4)垃圾回收子系统(Garbage Collection System)之G1垃圾收集器SATB

通过G1SATBCardTableModRefBS::enqueue(oop pre_val)把原引用保存到satb mark queue中,和RSet的实现类似,每个应用线程都自带一个satb mark queue.

在下一次的并发标记阶段,会依次处理satb mark queue中的对象,确保这部分对象在本轮GC是存活的。