Java详解对象终止方法finalize()的用法
作者:羡羡ˇ
finalize()方法机制
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() 方法只会被执行一次, 这时对象只能等待死亡
到此这篇关于Java详解对象终止方法finalize()的用法的文章就介绍到这了,更多相关Java finalize()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!