Java详解对象终止方法finalize()的用法(java对象被gc回收前会执行finalize方法并运行结束)

网友投稿 428 2022-07-31


finalize()方法机制

java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。

当GC去回收垃圾时, 总会在即将回收之前调用这个对象的 finalize()方法 , 一个对象finalize()方法只会被调用一次

finalize()方法可以被重写,通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。

我们一般最好不要主动去调用对象的finalize()方法, 理由有以下三点 :

1.在 finalize()时可能会导致对象复活。

2.finalize()方法的执行时间是没有保障的,它完全由 GC 线程决定,极端情况下,若不发生 GC,则 finalize()方法将没有执行机会。

3.一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalhYQlELize 是个死循环。

为什么会有这种机制呢 ?

我们先来了解 jvm 为对象定义的三种状态

第一次被 jvm 标为垃圾的对象此时处于"缓刑"阶段, 也就是说它此时并不是非死不可的

可触及的:从根节点开始,可以到达这个对象。

可复活的:对象的所有引用都被释放,但是对象有可能在 finalize()中复活。

不可触及的:对象的 finalize()被http://调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为 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() 方法只会被执行一次, 这时对象只能等待死亡


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springboot+thymeleaf整合阿里云OOS对象存储图片的实现
下一篇:Java使用线程池执行定时任务(线程池如何执行任务)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~