why:
CMS和G1都存在并发标记期间对象引用发生改变,从而导致部分白色对象标记不到(被视为了非活跃对象),被清理掉。
CMS和G1各自采用了不同的方案来解决。
例如:
引用关系改为:B->D的引用,改为了A->D和B->C。
how:
CMS:incremental update算法
incremental update是如果有一个黑色对象引用到了一个白色对象,即引用终点,即对D来说,引用它的源由原来的B变为了A,是从目标来解决的(A是新引用的最终对象)。通过将黑色对象重新标记为灰色对象,让collecter重新扫描,活着通过mod-union table来标记(即标记过了就不再标记)。
G1: SATB算法:
SATB认为开始并行标记时活的对象,在本次GC中都是活的对象。B->D的引用改为B->C,通过write barrier写屏障,会将B->D的引用也推入GC遍历的堆栈中标记,从而可以保证D还是能够被标记为活的。相对于D来说,引用它的源由原来的B变为了A,SATB是从源头来解决的(B的引用处开始解决的)。
如果把B=null,虽然D成了永久垃圾,但是SATB本次GC还是会认为D是活的,不回收成为浮动垃圾,直到下次GC。
扩展:
G1为何使用SATB算法?
1、G1的每个region中都有一个RSet,这个RSet存储的都是引用本region对象的引用,所有获得SATB只需要读region的RSet就可以获得。
2、remark阶段SATB算法更快,原因是:remark阶段SATB算法只处理那些被删除的引用,即并行标记开始引用,后面不引用了。(CMS的remark阶段除了删除的,还处理了新增引用,emark阶段扫描dirty card 和gc root(已经扫过的不扫),所以浮动垃圾stab是更多的)