可达性分析算法与三色标记法

目录

可达性分析算法

可达性算法实现细节

根节点的枚举

安全点

安全区

记忆集与卡表

写屏障

三色标记法

增量更新与原始快照

总结


        三色标记算法是一种基于可达性分析的算法,两者都是用于判断对象是否“死亡”(不会再被引用了),两者的区别就是,三色标记法是支持并发的。具有里程碑意义的CMS垃圾收集器就用到了三色标记法来判别对象,今天就给大家分享一下我的理解,如有不足,敬请指出。

可达性分析算法

        可达性分析算法的基本思路就是通过被称为“GC Roots”的 根节点开始,沿着引用关系开始搜集。如果某个对象GC Roots间没有任何引用链相连就叫做不可达,会被回收。

哪些对象可以作为 GC Roots ?

在Java技术体系中,固定可作为 GC Roots的对象包括以下几种:

  • 在虚拟机栈中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 在方法区中常量引用的对象。
  • 在本地方法中JNI(Native方法)引用的对象。
  • 被同步锁持有的对象。

        可达性分析算法的思路我们已经知道了,那么当我们在进行可达性分析的时候,如何快速的找到 GC Roots 并在一个合适的地方(引用链不在变化)开始搜寻,并能够解决“跨代引用”(当前区域回收时,可能该对象并不在引用链中,但是在其他未进行垃圾收集的区域如:老年代中有该对象的引用)的问题的呢?

        

可达性算法实现细节

根节点的枚举

        根节点的枚举要求在一个能够保障一致性的快照中进行。这里的一致性就是指整个枚举期间,执行子系统就像是被冻结在某个时间点了上,不会出现分析过程中,根节点的集合对象引用关系还在不停的变化。

        在虚拟机中会有一组称为 OopMap 的数据结构,里面记录了哪些地方存在着对象引用。一旦类加载完成之后,JVM就会把对象内什么偏移量上是什么类型的数据给计算出来。这样收集器在扫描的时候就可以直接得知这些信息了,并不需要真正从上面说的固定可作为的GC Roots的对象开始找了。

安全点

        安全点是指程序中可以进行垃圾回收操作的特定位置,程序执行过程中的某个特定位置,垃圾回收器可以安全地进行垃圾回收操作的点。在安全点处,所有的线程都被暂停,确保没有任何线程在执行代码,以便进行垃圾回收。除以以外,为了避免在每一个改变引用关系指令而生成的OopMap带来的空间消耗,所以安全点稳定不变的引用关系也成了生成OopMap的位置。

安全区

        安全区是指程序执行过程中的一段代码区域,在该区域内的代码可以被安全地中断并暂停执行。当垃圾回收器需要运行时,它会首先等待所有线程进入安全区,然后中断它们的执行。这样可以确保所有的线程都在安全区内停止,并且没有线程在执行可能破坏垃圾回收操作的代码。

记忆集与卡表

        记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。卡表则是将内存区域分为一块块大小固定的内存块,称为“卡”。卡表通常是一个图或类似的数据结构,里面的元素会反应引用关系是否被修改。通过记忆集利用卡表来定位就可以缩小GC Roots的扫描范围。

写屏障

        写屏障就是用来维护上面所说的卡表的,在引用改变动作发生的时刻,写屏障会在这个动作的AOP切面进行对卡表的维护。

        至此,可达性分析算法通过一系列的新技术实现了,快速定位到 GC Roots 后,在引用关系不变的情况下,连同跨代引用情况在内的对象诊断完成。

        而随着堆容量的增大,存储的对象变多,对象图结构的复杂,冻结用户线程运行带来的等待就会越来越长。而“标记”又是所有追踪式垃圾收集算法的共同特征,这时候就需要设计新的算法了。

三色标记法

        想解决停顿时间过长,我们需要先明白为什么可达性分析算法需要在一个能够保障一致性的快照中进行。我们引用三色标记来辅助。

  • 白色:表示对象尚未被垃圾收集器访问过,仍然是白色的对象,即代表不可达。
  • 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经被扫描过了,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
  • 灰色:表示对象已经被垃圾收集器访问过了,但这个对象上至少存在一个引用还没被扫描过。

        关于可达性分析的扫描过程,如果此时用户线程不是冻结的而是与收集器并发工作,而恰好唉收集器标记颜色的时候,用户线程在修改引用关系——修改图的结构。这样就会导致两个问题,一个是误留,将本应该消亡的对象被标记为存活,这虽然不是什么好事,但也是可以容忍的,只不过产生了部分浮动垃圾,下次GC清理掉就行。另一个就是误删,将本应该存活的对象标记为消亡,这是非常致命的后果,程序肯定会因此发生错误。

增量更新与原始快照

        而 Wilson 在1994年在理论上证明了,当且仅当同时满足以下两个条件的时候,会产生对象消失的问题。就是本应该是黑色的对象被标记为白色。

  • 插入了一条或多条从黑色对象到白色对象的新引用;
  • 删除了全部从灰色对象到该白色对象的直接或间接引用;

        因此,只要破坏其中任意一个条件就可以解决对象消失的问题。由此也分别产生了两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The Beginning,SATB)。

        增量更新要破坏的是第一个条件。当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束以后,再将这些记录过引用关系中的黑色对象为根,并将他们当成灰色对象重新扫描一次。

        原始快照要破坏的是第二个条件。当灰色对象要删除白色对象的引用关系时,就将这个跟要删除的引用记录下来,在并发扫描结束后,再将记录过的引用关系中的灰色对象为根,重新扫描一次。简单理解就是,无论引用关系删除与否,都会按照刚开始扫描的那一刻的对象图快照进行搜索。

        上面两种方法对引用的记录都是通过,写屏障实现的。

总结

        至此我们明白了可达性分析算法实现的具体细节与三色标记法为什么可以减少标记停顿时间。回顾一下可达性分析算法是通过对GC Roots 的引用链进行搜寻,将不在引用链中的判定为“死亡”。在安全点安全区(保证引用关系不变)停下后,通过OopMap快速找到引用位置,然后通过记忆集和卡表(写屏障来维护)缩减跨代引用的搜寻范围,加速对象的判断与内存的回收。然后通过增量更新和原始快照两个技术,解决了可达性分析算法对分析过程必须在保障一致性的快照中的需求,减少了时间停顿(不是完全不停顿,只是停顿时间很短)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值