G1的SATB
本文受到狼哥占小狼的启发,https://www.jianshu.com/p/9e70097807ba
在上一篇 G1的GC模式中介绍了在最终标记阶段 会使用SATB算法进行最终的标记 https://www.cnblogs.com/juniorMa/articles/13879270.html。
但关于SATB的一些详细知识还没有介绍,本文就继续介绍什么是SATB,SATB能解决的问题.
一 三色标记法
先说说三色标记法
为了解决在并发标记过程中,存活对象漏标的情况,GC HandBook把对象分成三种颜色:
- 黑色:自身以及可达对象都已经被标记
- 灰色:自身被标记,可达对象还未标记
- 白色:还未被标记
G1之所以很快,就是因为G1不会对一个已经是黑色的对象变成灰色,即使它的成员会在并发标记内指向一个新的对象。减少了最终标记的工作量。
而它能够做到就是依赖SATB算法。
说回三色标记,三色标记法要解决的问题就是错标和漏标。这里先明确一个概念,在垃圾收集过程中被标记过的才是活对象,没标记的就是垃圾,也就是白色对象。
错标就是指它是垃圾,结果在本轮没被回收,这个还是可以接受的,毕竟下一轮还是能回收的。
但是漏标就严重了,一个没有被标记过的对象会被当成垃圾回收,这就会导致程序出错了。
漏标的情况只会发生在白色对象中,且满足以下任意一个条件:
- 并发标记时,应用线程给一个黑色对象的引用类型字段赋值了该白色对象
- 并发标记时,应用线程删除所有灰色对象到该白色对象的引用,也就是赋值为NULL
G1的解决办法是这样的,对于1 利用post-write barrier,记录所有新增的引用关系,然后根据这些引用关系为根重新扫描一遍。那么什么是post-write barrier呢?就是大名鼎鼎的Result Set了。
对于2 则是利用pre-write barrier,将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根重新扫描一遍。
这就会让本来应该死的对象活下来,会增加浮动垃圾但是可以减少做标记的工作量加快速度。
二 SATB
- Anything live at Initial Marking is considered live.
- Anything allocated since Initial Marking is considered live.
接下来就详细的解释下这两句话是怎么回事。
1 Anything live at Initial Marking is considered live.
write_barrier_slot(Object* src, Object** slot, Object* new_ref) { old_ref = *slot; if( !is_marked(old_ref) ){ enqueue(old_ref); } *slot = new_ref; }
此时保存旧引用,这样旧引用对象就会被标记到,也就是上面说的 Anything live at Initial Marking is considered live.
2 Anything allocated since Initial Marking is considered live.
Region包含了5个指针,分别是bottom、previous TAMS、next TAMS、top和end

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详细流程
-
SATB是维持并发GC的一种手段。G1并发的基础就是SATB。SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对像就认为是活的,从而开成一个对象图。
-
在GC收集的时候,新生代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象。
-
如何找到在GC过程中分配的对象呢?每个region记录着两个top-at-mark-start(TAMS)指针,分别为prevTAMS和nextTAMS。在TAMS以上的对象就是新分配的,因而被视为隐式marked。
-
通过这种方式我们就找到了在GC过程中新分配的对象,并把这些对象认为是活的对象。
-
解决了对象在GC过程中分配的问题,那么在GC过程中引用发生变化的问题怎么解决呢?
-
G1给出的解决办法是通过Write Barrier。Write Barrier就是对引用字段进行赋值做了额外处理。通过Write Barrier就可以了解到哪些引用对象发生了什么样的变化。
-
mark的过程就是遍历heap标记live object的过程,采用的是三色标记算法,这三种颜色为white(表示还未访问到)、gray(访问到但是它用到的引用还没有完全扫描)、back(访问到而且其用到的引用已经完全扫描完)。
-
整个三色标记算法就是从GC roots出发遍历heap,针对可达对象先标记white为gray,然后再标记gray为black;遍历完成之后所有可达对象都是balck的,所有white都是可以回收的。
-
SATB仅仅对于在marking开始阶段进行“snapshot”(marked all reachable at mark start),但是concurrent的时候并发修改可能造成对象漏标记。并非所有对象,而是开始阶段的存活对象
-
对black新引用了一个white对象,然后又从gray对象中删除了对该white对象的引用,这样会造成了该white对象漏标记。
-
对black新引用了一个white对象,然后从gray对象删了一个引用该white对象的white对象,这样也会造成了该white对象漏标记。
-
对black新引用了一个刚new出来的white对象,没有其他gray对象引用该white对象,这样也会造成了该white对象漏标记。
1 G1标记分为快照部分和增量部分。
浙公网安备 33010602011771号