首先标记有三个阶段:
初始标记 -> 并发标记 -> 最终标记 -> (拷贝存活区域对象)
标记复制算法中的标记阶段所用到的标记算法(三色标记算法);
GC如果想查找到存活的对象,根据GCRoot可达分析算法 根据GCRoot引用链遍历存活对象。根据GCRoot遍历过程中, 按照是否访问过该对象 分为三种不同颜色。
白色:本对象没有访问过(没有被GCRoot扫描过)(有可能是为垃圾对象);
灰色:本对象已经被访问过(被GCRoot扫描过),且本对象的所有属性没有访问过;本对象所有属性都访问过后,本对象有灰色变为黑色。
黑色:本对象已经被访问过(被GCRoot扫描过),且本对象的所有属性都被访问过;
三色标记法 - 模型 三色标记法 - 模型
// 演示模型中的对象引用
class B{
private C c;
}
class C{
private D d;
}
class D{
}
1、初始标记时,所有对象都在白色容器中;
2、并发标记时(用户线程与GC线程同时运行),将本对象引用到的其他对象移动灰色容器中,如果本对象没有引用到其他对象或者其他对象已经标记过,则该对象放到黑色容器中。
3、重复以上这些操作,到灰色容器为空时,则停止。
4、结束后,如果在白色容器中任然存在的对象,则认为就是与GCRoot没有直接关联,则认为就是为不可达对象,可以被垃圾回收线程清理。
在并发标记阶段会出现漏标问题, 由于是GC线程和用户线程同时运行的, 参考下图模型:
当遍历的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:
原始快照简单理解为备份 (备份被断开引用的白色对象)
如果用户线程在灰色C对象断开一个白色E对象的时候, 会记录原始快照(断开的白色E对象),
在重新标记阶段时候比白色E对象变为灰色为起始点扫描整个链.
- 好处: B黑色对象引用了E灰色对象, 则直接将E灰色变为黑色, 不用遍历整个引用链.
- 坏处: E灰色对象如果为垃圾对象, 则此次垃圾回收不会被清除掉(浮动垃圾), 只能下次清除.
简单说就是会重新扫描黑色对象的整个引用链。(关联了白色对象的黑色对象)
满足了第一个条件(灰色对象不在关联白色对象的时候,当黑色对象如果关联了白色对象的时候会记录该黑色对象,
然后在重新标记的时候,将记录的黑色对象变为灰色,从新开始修正标记,但是这种方案能够确保垃圾都被清理,缺点就是效率非常低,
因为会扫描到整个黑色对象所有引用。
- 好处: 不会产生浮动垃圾
- 坏处: 会扫描整个引用链, 比较耗时.
G1采用原始快照 - 会产生浮动垃圾, 不会扫描整个引用链, 效率快;
CMS采用增量更新 - 不会产生浮动垃圾, 会扫描整个引用链,效率慢;
相对于来说原始快照方式比增量更新方式容易产生浮动垃圾,但是执行效率比增量更新要高。
留言与评论(共有 0 条评论) “” |