时间:2022-12-28 09:39:44 | 栏目:JAVA代码 | 点击:次
Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
当GC去回收垃圾时, 总会在即将回收之前调用这个对象的 finalize()方法 , 一个对象finalize()方法只会被调用一次
finalize()方法可以被重写,通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。
我们一般最好不要主动去调用对象的finalize()方法, 理由有以下三点 :
1.在 finalize()时可能会导致对象复活。
2.finalize()方法的执行时间是没有保障的,它完全由 GC 线程决定,极端情况下,若不发生 GC,则 finalize()方法将没有执行机会。
3.一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalize 是个死循环。
为什么会有这种机制呢 ?
我们先来了解 jvm 为对象定义的三种状态
第一次被 jvm 标为垃圾的对象此时处于"缓刑"阶段, 也就是说它此时并不是非死不可的
可触及的:从根节点开始,可以到达这个对象。
可复活的:对象的所有引用都被释放,但是对象有可能在 finalize()中复活。
不可触及的:对象的 finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为 finalize()只会被调用一次。
以上 3 种状态中,是由于 finalize()方法的存在,进行的区分。只有在对象不可触及时才可以被回收
可触及的, 意思就是说, 对象此时存在引用链, 是存活的, 可复活的意思是说, 此对象虽然已经被GC标为了垃圾, 但是此时未调用 finalize() 方法, 这个对象是有可能在finalize()中复活的. 不可触及的就是说, 此时finalize()方法已经被调用过了(没有复活), 这个对象最终的命运已经是非死不可了, 只能静等GC去回收它
那么具体的过程是怎样的呢?
判定一个对象 objA 是否可回收,至少要经历两次标记过程:
1.如果对象 objA 到 GC Roots 没有引用链,则进行第一次标记。
2.进行筛选,判断此对象是否有必要执行 finalize()方法
如果对象 objA 没有重写 finalize()方法,或者 finalize()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA 被判定为不可触及的。
如果对象 objA 重写了 finalize()方法,且还未执行过,那么 objA 会被插入到 F-Queue 队列中,由一个虚拟机自动创建的、低优先级的 Finalizer 线程触发其 finalize()方法执行。finalize()方法是对象逃脱死亡的最后机会,稍后 GC 会对 F-Queue 队列中的对象进行第二次标记。如果 objA 在 finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,objA 会被移出“即将回收”集合。之后,对象会再次出现没有引用存在的情况. 在这个情况下,finalize()方法不会被再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的 finalize()方法只会被调用一次。
接着我们用代码演示对象的复活
public class CanReliveObj { public static CanReliveObj obj;//类变量,属于 GC Root //此方法只能被调用一次 @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("调用当前类重写的finalize()方法"); obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象obj建立了联系 } public static void main(String[] args) { try { obj = new CanReliveObj(); // 对象第一次成功拯救自己 obj = null; System.gc();//调用垃圾回收器 System.out.println("第1次 gc"); // 因为Finalizer线程优先级很低,暂停2秒,以等待它 Thread.sleep(2000); if (obj == null) { System.out.println("obj is dead"); } else { System.out.println("obj is still alive"); } System.out.println("第2次 gc"); // 下面这段代码与上面的完全相同,但是这次自救却失败了 obj = null; System.gc(); // 因为Finalizer线程优先级很低,暂停2秒,以等待它 Thread.sleep(2000); if (obj == null) { System.out.println("obj is dead"); } else { System.out.println("obj is still alive"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
执行结果 :
先将引用指向 null , 这时第一次GC , 我们重写了finalize()方法, 致使对象在第一次垃圾回收时成功自救, 第二次再将引用指向null , 因为finalize() 方法只会被执行一次, 这时对象只能等待死亡