page contents

关于CMS重新标记阶段长时间STW的问题

Pack 发布于 2020-02-24 15:25
阅读 504
收藏 0
分类:Java开发

R大对SATB的解释中有如下一部分内容:

CMS的incremental update设计使得它在remark阶段必须重新扫描所有线程栈和整个young gen作为root;G1的SATB设计在remark阶段则只需要扫描剩下的satb_mark_queue ,解决了CMS垃圾收集器重新标记阶段长时间STW的潜在风险。


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

1)黑色:自身以及可达对象都已经被标记

2)灰色:自身被标记,可达对象还未标记

3)白色:还未被标记

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

1)并发标记时,应用线程给一个黑色对象的引用类型字段赋值了该白色对象

2)并发标记时,应用线程删除所有灰色对象到该白色对象的引用


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

对于第二种情况,利用pre-write barrier,SATB利用pre-write barrier将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根STW地重新扫描一遍即可避免漏标问题。


我的理解是,CMS是用到了post-write barrier,G1是用到了pre-write barrier,既然一个是扫描新增的引用关系,一个是扫描旧的被删除的引用关系,为什么R大原话是CMS在remake阶段要扫描所有线程栈和整个young gen作为root,post-write barrier直接扫描已经被记录的新增的那部分引用关系不就好了,何必扫描全部线程栈和整个young gen?

438
Pack
Pack

CMS是老年代收集器,并不是全代收集器,HotSpot没有专门用于记录年轻代—>老年代的引用信息,所以既然CMS只收集老年代,干脆就扫描整个年轻代都作为root,简单粗暴。还有一个原因是因为在记录引用的card table中,整个堆使用的是同一份,每一次的YGC都会改变card table的内容,这样在CMS执行remark的时候,card table中的信息就几乎无参考价值,所以可以简单的将card table理解为只对YGC服务
去翻了一下HotSpot的代码,对于post barrier是这样写的:
void post_write_barrier(oop* field, oop val) {
jbyte* card_ptr = card_for(field);
*card_ptr = dirty_card;
}
这里的field压根儿看不出来是处于哪一代中,也未关心val是什么,所以可以得出card table是记录了整个堆中对象引用的变动,YGC过于频繁,所以这个card table中关于年轻代的对象引用信息变化很快,对老年代来说没有多大用处,与其到card table中去找年轻代的对象引用变化,不如直接将整个年轻代的对象都作为root

请先 登录 后评论