三色标记
前言
三色标记相信大家也有所耳闻,但是都没有过深入的了解。小编在之前和同事交流的过程中,了解到很多大厂在问虚拟机问题的时候会问到三色标记的问题。所以想再今天和大家分享一下自己对三色标记的理解。如果有理解的有问题的地方,欢迎大家指正。那么就让我们进入三色标记的学习中吧!
提示:以下是本篇文章正文内容,下面案例可供参考
一、三色标记是什么?
三色标记算法是把在Gc Root可达性分析算法遍历对象的过程中,遇到的对象进行“黑,灰,白”三种颜色的标记。
三色标记操作的阶段是在CMS的并发标记阶段,因为用户的线程也在执行,所以已经标记的对象的引用可能会发生变化。**多标和漏标**的情况都会发生,漏标主要是通过三色标记算法来解决的。
二、三色的介绍
1.黑色
黑色表示对象已经被垃圾收集器访问过,并且这个对象的所有引用都被扫描到了。黑色对象代表已经扫描过,是安全存活的,如果有其他对象的引用指向黑色的对象,不需要重新扫描。
2.灰色
灰色表对象已经被垃圾收集器访问过,但是这个对象的部分引用未被访问到。
3.白色
白色表示对象还没有被垃圾收集器访问过。显然处于可达性分析的初始阶段,如果可达性分析已经结束了,对象还是标记为白色则认为这个对象的垃圾对象。
上图关系的维护都在并发标记阶段维护,在重新标记阶段处理,这两种方式都是处理漏标的情况
- 在上图中对象A到对象D新增关联关系,在HotSpot虚拟机中会新增一个写屏障,用于记录新增关系,内部维护的是一个集合。当在重新标记的阶段,就会扫描整个集合。也就是三色标记中的增量更新。
- 在上图中对象B到对象D删除了关联关系,在HotSpot虚拟机中会新增一个写屏障,用于记录对象间删除的关系。在重新标记阶段,扫描集合,将删除的对象直接标记为黑色(浮动垃圾),等待下次Gc再去回收。这个是三色标记中的原始快照
三、浮动垃圾
浮动垃圾的产生在下一次Gc过程中再去标记收集
- 在并发标记阶段,如果运行的方法结束,栈帧被销毁,栈帧中的局部变量作为Gc Root遍历的引用对象集在之前标记为非垃圾对象,但是实际上目前是垃圾对象。本轮的并发清除阶段不会被清除。这类被称为浮动垃圾
- 在并发标记和并发清除阶段,新生成的对象会被直接标记成黑色,等待下次Gc再被回收。这类也称为浮动垃圾
- 在并发标记阶段,如果对象之间的关系发生了删除,这时候虚拟机内部通过写屏障维护一个集合,这个集合在重新标记阶段会把所以删除关系的引用对象,设置成黑色。这类也是浮动垃圾
四、读写屏障
1.为什么HotSpot虚拟机需要提供一个读写屏障呢?带着疑问去学习我们能了解的更快。
读写屏障主要解决的问题是漏标的问题。漏标对象会导致对象被当成垃圾回收掉。这对于任何程序都是很严重的BUG。必须要解决。因此HotSpot虚拟机为我们提供了两种方案来解决这个问题:增量更新和原始快照
- 增量更新:增量更新就是在黑色对象插入新的白色对象的引用时,就把这个引用记录下来。等并发扫描后,重新以黑色对象为根开始扫描整个引用链。也可以理解为当黑色对象插入新的白色引用时,会变成灰色。
- 原始快照:原始快照就是在灰色对象要删除白色对象的引用时,就把这个引用记录下来。等扫描结束后,将记录中的引用关系为灰色的为根进行再次扫描,将扫描到的白色对象直接设置成黑色。(产生的浮动垃圾在下次Gc的时候再去收集)
2.以上引用关系的新增和删除,都是通Hotspot虚拟机的写屏障实现的
3.说了那么多,那么写屏障在HotSpot虚拟机中到底是如何实现的呢?
所谓的写屏障,其实就是在赋值阶段的前后,加入一些操作。(思路可以参考Spring中的AOP)
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field); // 写屏障-写前操作
*field = new_value;
post_write_barrier(field, value); // 写屏障-写后操作
}
写屏障实现STAB,在写前记录下原来的引用对象。
void pre_write_barrier(oop* field) {
oop old_value = *field; // 获取旧值
remark_set.add(old_value); // 记录原来的引用对象
}
写屏障实现增量更新,在写后记录下对象间新增引用。
void post_write_barrier(oop* field, oop new_value) {
remark_set.add(new_value); // 记录新引用的对象
}
四、总结
CMS垃圾收集器并发收集阶段漏标的解决方案:写屏障+增量更新。
后面我还会介绍G1垃圾收集器的原理,和CMS垃圾收集器还是有很多相似的地方。这边小小的透露下G1垃圾收集器在并发扫描阶段出现了漏标会采用:写屏障+原始快照的方案。
那么为什么G1会选择这种方案呢?大家可以思考一下。顺带大家在思考一个阿里的面试题:为什么G1要选择使用原始快照,而cms选择使用增量更新呢?