并发标记带来了什么问题?
在说带来什么问题之前,我们必须得先搞清楚一个问题:
为什么遍历对象图的时候必须在一个能保障一致性的快照中?
为了说明这个问题,我们就要引入"三色标记"大法了。注意:"三色标记"也是jvm的一个考点哦。
什么是"三色标记"?《深入理解Java虚拟机(第三版)》中是这样描述的:
在遍历对象图的过程中,把访问都的对象按照"是否访问过"这个条件标记成以下三种颜色:
白色:表示对象尚未被垃圾回收器访问过。显然,在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。
黑色:表示对象已经被垃圾回收器访问过,且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,它是安全存活的,如果有其它的对象引用指向了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
灰色:表示对象已经被垃圾回收器访问过,但这个对象至少存在一个引用还没有被扫描过。
读完上面描述,再品一品下面的图:
可以看到,灰色对象是黑色对象与白色对象之间的中间态。当标记过程结束后,只会有黑色和白色的对象,而白色的对象就是需要被回收的对象。
在可达性分析的扫描过程中,如果只有垃圾回收线程在工作,那肯定不会有任何问题。
但是垃圾回收器和用户线程同时运行呢?这个时候就有点意思了。
垃圾回收器在对象图上面标记颜色,而同时用户线程在修改引用关系,引用关系修改了,那么对象图就变化了,这样就有可能出现两种后果:
一种是把原本消亡的对象错误的标记为存活,这不是好事,但是其实是可以容忍的,只不过产生了一点逃过本次回收的浮动垃圾而已,下次清理就可以。
一种是把原本存活的对象错误的标记为已消亡,这就是非常严重的后果了,一个程序还需要使用的对象被回收了,那程序肯定会因此发生错误。
当面试官问你:为什么会产生浮动垃圾的时候,你就可以用上面的话来回答。
但是大概率情况下面试官应该更加关心第二种情况。
他可能会问:你刚刚说的第二种情况,"把原本存活的对象错误的标记为已消亡"能具体的说明一下吗?怎么消亡的?垃圾回收器是怎么解决这个问题的?
所以接下来,我们主要分析一下并发标记的过程中"对象消失"的问题。具体"对象"是怎么没了的。
这里借助《深入理解Java虚拟机(第三版)》的示例,但是第三版的示例的描述写的不是特别容易理解,我就尽我所能的描述的清楚一些,下面会结合动图,分析标记的三种情况:
正常标记
我们先看一下一次正常的标记过程:
首先是初始状态,很简单,只有GC Roots是黑色的。同时需要注意下面的图片的箭头方向,代表的是有向的,比如其中的一条引用链是:
根节点->5->6->7->8->11->10
此时,黑色对象是存活的对象,白色对象是消亡了,可以回收的对象。
记住,上面演示的是一切都是那么美好的正常情况。
对象消失的情况一
接下来,我们看看对象消失的情况:
如果用户线程在标记的时候,修改了引用关系,就会出现下面的情况:
这时,我们和之前分析的正常扫描结束的对象图对比,就能清楚的看到,扫描完成后,原本还在被对象5引用的对象9,由于是白色对象,所以根据三色标记原则,对象9会被当成垃圾回收。
这样就出现了对象消失的情况。