G1和CMS的三色标记法及漏标问题

首先标记有三个阶段:

初始标记 -> 并发标记 -> 最终标记 -> (拷贝存活区域对象)

  • 初始标记: 只会标记GCRoot直接关联的对象
  • 并发标记: 基于初始标记时标记的对象作为起点, 标记所有(属性)关联的对象
  • 最终标记: 处理漏标问题 (并发标记阶段, 漏标的GCRoot可达的对象)

什么是三色标记

标记复制算法中的标记阶段所用到的标记算法(三色标记算法);

GC如果想查找到存活的对象,根据GCRoot可达分析算法 根据GCRoot引用链遍历存活对象。根据GCRoot遍历过程中, 按照是否访问过该对象 分为三种不同颜色。

白色:本对象没有访问过(没有被GCRoot扫描过)(有可能是为垃圾对象);

灰色:本对象已经被访问过(被GCRoot扫描过),且本对象的所有属性没有访问过;本对象所有属性都访问过后,本对象有灰色变为黑色。

黑色:本对象已经被访问过(被GCRoot扫描过),且本对象的所有属性都被访问过;


G1和CMS的三色标记法及漏标问题

三色标记法 - 模型 三色标记法 - 模型

// 演示模型中的对象引用
class B{
  private C c;
}
 
class C{
  private D d;
}
 
class D{
}

原理:

1、初始标记时,所有对象都在白色容器中;


2、并发标记时(用户线程与GC线程同时运行),将本对象引用到的其他对象移动灰色容器中,如果本对象没有引用到其他对象或者其他对象已经标记过,则该对象放到黑色容器中。


3、重复以上这些操作,到灰色容器为空时,则停止。


4、结束后,如果在白色容器中任然存在的对象,则认为就是与GCRoot没有直接关联,则认为就是为不可达对象,可以被垃圾回收线程清理。

三色标记法为什么存在漏标问题


在并发标记阶段会出现漏标问题, 由于是GC线程和用户线程同时运行的, 参考下图模型:


G1和CMS的三色标记法及漏标问题

当遍历的C对象的时候,C对象已经存放到灰色容器中,突然用户线程修改C对象属性.E=Null; E对象与C对象断开,则E对象为垃圾对象,但是用户线程修改B对象的属性=E对象,在进行最终标记阶段时B对象已经为黑色不会继续遍历B对象,就会导致E对象会被垃圾线程清理,这个过程称作为漏标问题。

C.属性E=null;B.属性E=E;

漏标的问题满足两个条件:

1. 至少有一个黑色对象指向了白色对象

2. 所有灰色对象扫描完整个链时,删除之前所有的白色对象。

参考上图"漏标问题-模型",可能出现以下两种情况:

在并发标记阶段的时候, 用户线程与GC线程同时运行。

条件: GCRoot遍历到C节点, C节点变为灰色, B节点变为黑色;

情况1: GC线程先执行, 扫描到E对象, 为可达对象, 没有问题 (不会产生漏标问题);

情况2:

  1. 用户线程执行灰色对象C.E=null;
  2. GCRoot线程在执行的时候无法扫描到E对象,E对象是一个垃圾对象;
  3. 用户线程执行黑色对象B.E=E (导致漏标问题)。

漏标解决方案

G1收集器中处理漏标问题 (原始快照SATB)

原始快照简单理解为备份 (备份被断开引用的白色对象)

如果用户线程在灰色C对象断开一个白色E对象的时候, 会记录原始快照(断开的白色E对象),

在重新标记阶段时候比白色E对象变为灰色为起始点扫描整个链.


- 好处: B黑色对象引用了E灰色对象, 则直接将E灰色变为黑色, 不用遍历整个引用链.

- 坏处: E灰色对象如果为垃圾对象, 则此次垃圾回收不会被清除掉(浮动垃圾), 只能下次清除.

CMS收集器中处理漏标问题(增量更新)

简单说就是会重新扫描黑色对象的整个引用链。(关联了白色对象的黑色对象)

满足了第一个条件(灰色对象不在关联白色对象的时候,当黑色对象如果关联了白色对象的时候会记录该黑色对象,

然后在重新标记的时候,将记录的黑色对象变为灰色,从新开始修正标记,但是这种方案能够确保垃圾都被清理,缺点就是效率非常低,

因为会扫描到整个黑色对象所有引用。


- 好处: 不会产生浮动垃圾

- 坏处: 会扫描整个引用链, 比较耗时.

G1和CMS漏标处理对比

G1采用原始快照 - 会产生浮动垃圾, 不会扫描整个引用链, 效率快;

CMS采用增量更新 - 不会产生浮动垃圾, 会扫描整个引用链,效率慢;

相对于来说原始快照方式比增量更新方式容易产生浮动垃圾,但是执行效率比增量更新要高。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章