三色标软件(一文弄懂三色标记算法)
本文已收录至Github ,推荐阅读 ? Java随想录
微信公众号:Java随想录
CSDN: 码农BookSea
你愈是少说你的伟大 ,我将愈想到你的伟大 。——培根
面试官:我们先从JVM基础开始问 ,了解三色标记算法吗?
我:额......不了解 。
面试官:出去的时候记得把门带上 。
现在Java面试真的已经是越来越卷了 ,很喜欢问底层实现原理 。本篇来聊聊三色标记算法 ,也是Java面试的常客 。
三色标记算法可以扯出增量更新和原始快照 ,聊好了会让面试官觉得你这小伙子有点东西 。
三色标记算法
既然叫三色标记算法 ,首先我们要搞明白是哪三色 ,三色是:黑色 ,白色 ,灰色 。
把遍历对象图过程中遇到的对象 ,按照是否访问过这个条件标记成以下三种颜色:
白色:表示对象尚未被垃圾收集器访问过 。显然在可达性分析刚刚开始的阶段 ,所有的对象都是白色的,若在分析结束的阶段 ,仍然是白色的对象 ,即代表不可达 。 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过 。黑色的对象代表已经扫描过 ,它是安全存活的 ,如果有其他对象引用指向了黑色对象 ,无须重新扫描一遍 。黑色对象不可能直接(不经过灰色对象)指向某个白色对象 。 灰色:表示对象已经被垃圾收集器访问过 ,但这个对象上至少存在一个引用还没有被扫描过。原书中的图画的很好 ,一目了然 。
由于一些垃圾回收器存在垃圾回收线程和用户线程并发的情况(例如CMS的并发阶段) ,那么三色标记会有2个问题:
一种是把原本消亡的对象错误标记为存活 ,这不是好事 ,但其实是可以容忍的 ,只不过产生了一点逃过本次收集的浮动垃圾而已 ,下次收集清理掉就好 。 另一种是把原本存活的对象错误标记为已消亡 ,这就是非常致命的后果了 ,程序肯定会因此发生错误。Wilson于1994年在理论上证明了,当且仅当以下两个条件同时满足时 ,会产生“对象消失 ”的问题 ,即原本应该是黑色的对象被误标为白色:
赋值器插入了一条或多条从黑色对象到白色对象的新引用 。 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用 。因此,我们要解决并发扫描时的对象消失问题 ,只需破坏这两个条件的任意一个即可 。由此分别产生了两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The Beginning ,SATB) 。
这2种解决方案各破坏一个条件
增量更新
增量更新要破坏的是第一个条件 ,当黑色对象插入新的指向白色对象的引用关系时 ,就将这个新插入的引用记录下来 ,等并发扫描结束之后 ,再将这些记录过的引用关系中的黑色对象为根 ,重新扫描一次 。这可以简化理解为 ,黑色对象一旦新插入了指向白色对象的引用之后 ,它就变回灰色对象了 。
这其实有点像之前讲过类似OopMap的思想 ,本质也是维护了个映射关系 ,重新扫描的时候扫描这个映射关系就行了 ,不用全表扫描 。
原始快照
原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时 ,就将这个要删除的引用记录下来 ,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根 ,重新扫描一次 。这也可以简化理解为 ,无论引用关系删除与否 ,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索 。
以上无论是对引用关系记录的插入还是删除 ,虚拟机的记录操作都是通过写屏障实现的 。写屏障 ,我们之前讲记忆集与卡表的时候介绍过的 ,可以理解为Spring中的AOP ,目前为止卡表状态的维护 ,增量更新 ,原始快照都是基于写屏障 。
另外 ,CMS使用的是增量更新 ,G1使用的是原始快照 。
本篇文章就到这里 ,如果再遇见面试官问你类似的问题,你可以好好跟他扯皮咯。
如果本篇博客有任何错误和建议 ,欢迎给我留言指正 。文章持续更新 ,可以关注公众号第一时间阅读 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!