可达性算法标记的不可达对象一定会回收吗?

JVM垃圾回收算法 : 可达性分析

当前的主流商用编程语言(比如C#,Java等)的内存管理子系统都是通过可达性分析(Reachability Analysis)算法来判断一个对象是否存活。基本思路是通过一系列称为 “GC Roots” 的根对象作为起始标记节点集合,从 “GC Roots” 集合中的对象开始,根据引用关系向下进行搜索(从根到叶),每一条搜索的路径称为 “引用链” ,如果一个对象没有与任何的引用链相连接,或者说,这个对象无法从 “GC Roots” 到达,则该对象会被标记为垃圾。

在这里插入图片描述

图1 可达性分析算法标记结果

上图显示了根据可达性分析算法获得的对象标记结果图,由此可见,Object1 到 Object6 都可以从 “GC Root” 对象到达,所以这些对象还有用,无法被回收。而 Object7 到 Object13 无法从 “GC Root” 到达,即不包含在任何的引用链当中,所以被标记为垃圾对象,有JVM进行垃圾回收。

GC Roots 对象如何选取?

在Java技术体系中,如下对象可以固定作为GC Roots 对象:

  • 虚拟机栈中引用的对象、栈帧中本地变量表中引用的对象(比如:各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等)
  • 方法区中类的静态属性引用的对象 (比如:Java类的引用类型静态变量)
  • 方法区中常量引用的对象 (比如:字符串常量池中的引用)
  • 本地方法栈中 JNI (Native方法)引用的对象
  • Java虚拟机内部的引用 (比如:基本数据类型对应的Class对象、NullPonitException等常驻的异常对象、系统类加载器)
  • 所有被 Synchronized 持有的对象
  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等

除了上述固定的 GC Roots 集合外,根据用户所选的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象临时性的加入到 GC Roots 集合。

可达性分析算法标记的垃圾对象并非 “非死不可”

即使被可达性分析算法标记为垃圾的对象,并一定就会被GC回收,也就是不一定 “非死不可”。要真正宣告一个对象死亡(成为一定被回收的垃圾对象),至少要经历两次标记过程:

  • 引用链标记(第一次标记)
  • F-Queue标记(第二次标记)

如果对象在进行可达性分析后发现没有与任何的 “GC Roots” 引用链相连接,则会被第一次标记,随后会进行一次筛选过程,筛选的条件是 “该对象是否有必要执行 finalize() 方法”。只有在该对象***没有覆盖 finalize() 方法***,或者 finalize() 方法已经被虚拟机调用过 时,才认为是 “没有必要执行”

如果对象被判定为有必要执行 finalize() 方法,则会被放入 F-Queue 队列中,并在稍后由一条 由虚拟机自动创建、低调度优先级的Finalizer线程去 “执行” 他们的 finalize() 方法(虚拟机会触发该方法开始运行,但并不承诺一定会等到他运行结束,因为如果某个对象的 finalize() 方法执行的非常缓慢,或者发生了死循环,将很有可能导致 F-Queue 队列中的其他对象永久处于等待,甚至可能导致整个内存回收子系统的崩溃)。

收集器稍后会对 F-Queue 队列中的对象进行第二次小规模的标记,如果此时该对象重新与引用链上任何一个对象建立了关联,则该对象就可以逃脱死亡的命运,即可以不用被 GC 回收。(比如:把对象自己 (this) 赋值给某个类变量或者对象的成员变量,则在第二次标记时将会被移除 “即将回收” 的集合)。如果此时该对象还没有与任何引用链对象关联,则该对象无法逃脱死亡,基本上就真的要被 GC 回收了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一次调用 System.gc() 方法后,又将 object对象赋值给了它自己,所以第一次 if 判断条件为 true,执行了 isAlive() 方法。第二次 if 判断时,由于 finalize() 方法只会执行一次,所以调用 System.gc() 方法不会再次执行 finalize() ,所以第二次 if 时 object 为 null。

第一次调用 System.gc() 方法后,可以看到 object 对象并没有被 GC 回收掉,所以该对象成功逃脱了死亡,即在被收集前成功逃脱了。

总结

虽然 finalize() 方法可以拯救一个被可达性分析标记的垃圾对象不被回收,但是不推荐使用该方法,应该尽量避免使用。原因在于:它并不能等同于C和C++语言的析构函数,而是Java刚诞生时为了使传统的C、C++程序员更容易接受Java所做出的一项妥协。它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序,如今已被官方明确声明为不推荐使用的语法。



本文参考周志明老师的《深入理解Java虚拟机》
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值