什么是三色标记
三色标记是在cms和g1中使用的垃圾追踪算法
-
黑色
从GCRoots开始,已扫描过它全部引用的对象,标记为黑色
-
灰色
扫描过对象本身,还没完全扫描过它全部引用的对象,标记为灰色
-
白色
还没扫描过的对象,标记为白色
所以,从GCRoots开始,顺着一直向下扫描,用可达性分析算法,最后所有的白色对象,都是垃圾对象,可以回收
三色标记的漏标问题
我们采用一个最简单的模型,只有三个对象
-
某个状态下,黑色->灰色->白色
-
如果一切顺利,不发生任何引用变化,gc线程顺着灰色的引用向下扫描,最后都变成黑色,都是存活对象
-
但是如果出现了这样一个状况,在扫描到灰色的时候,还没有扫描到这个白色对象,此时,黑色对象引用了这个白色对象,而灰色对象指向了别人,或者干脆指向了null,也就是取消了对白色对象的引用
4. 那么我们会发现一个问题,根据三色标记规则,gc
会认为,黑色对象是本身已经被扫描过,并且它所有指向的引用都已经被扫描过,所以不会再去扫描它有哪些引用指向了哪些对象
然后,灰色对象因为取消了对白色对象的引用,所以后面gc开始扫描所有灰色对象的引用时候,也不会再扫描到白色对象
最后结果就是,白色对象直到本次标记扫描结束,也是白色,根据三色标记规则,认为它是垃圾,被清理掉
但是实际情况,它明显是被引用的对象,是绝对不能当做垃圾来清除的,因为漏标,最后被当垃圾清理掉了
漏标的两个充要条件
- 有至少一个黑色对象在自己被标记之后指向了这个白色对象
- 所有的灰色对象在自己引用扫描完成之前删除了对白色对象的引用
这两个条件,必须全满足,才会造成漏标问题.
换言之,我们破坏任何一个条件.这个白色对象,就不会再被漏标
这样就产生了两个解决办法
CMS采用的是增量更新
增量更新破坏的是第一个条件,我们在这个黑色对象增加了对白色对象的引用之后,将它的这个引用,记录下来,在最后标记的时候,再以这个黑色对象为根,对它的引用进行重新扫描.
可以简单理解为,当一个黑色对象增加了对白色对象的引用,那么这个黑色对象就被变灰
这样有一个缺点,就是会重新扫描这个黑色对象的所有引用,比较浪费时间
G1采用的是原始快照
原始快照破坏的是第二个条件,我们在这个灰色对象取消对白色对象的引用之前,将这个引用记录下来,在最后标记的时候,再以这个引用指向的白色对象为根,对它的引用进行扫描
可以简单理解为,当一个灰色对象取消了对白色对象的引用,那么这个白色对象被变灰
这样做的缺点就是,这个白色对象有可能并没有黑色对象去引用它,但是它还是被变灰了,就会导致它和它的引用,本来应该被垃圾回收掉,但是此次GC存活了下来,就是所谓的浮动垃圾
.其实这样是比较可以忍受的,只是让它多存活了一次GC而已,浪费一点点空间,但是会比增量更新更省时间.