java为什么不推荐覆写Object的finalize方法
Posted wen-pan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java为什么不推荐覆写Object的finalize方法相关的知识,希望对你有一定的参考价值。
说明:
- 在JVM中除了
强、软、弱、虚
这四种引用以外,其实还有一种类型的引用,这种引用非常不常见,在日常开发中基本99.9999999999%不会用,在JVM回收覆写了finalize方法
的对象时会用到这种引用。这种引用就是【终结器引用】。 - java提供了一个finalize方法,可以帮助我们进行资源释放,类似于C++中的析构函数。但是目前普遍的认识是不要使用,为什么呢?就是因为对java虚拟机的垃圾回收有影响。
1、为什么不推荐使用覆写Object的finalize方法
Object类的finalize方法被重写时,希望在这个对象被垃圾回收的时候调用这个finalize方法来完成一些资源释放或其他操作,但这是非常不可取的。为什么?看下面流程分析!
2、finalize回收流程
假设A对象
覆写了finalize方法
- 在A对象在初始化的过程中会判断是否重写了finalize方法,方法是判断两个字段标志
has_finalizer_flag
和RegisterFinalizersAtInit
- 在第一次垃圾回收时,GC垃圾收集器发现没有强引用 引用
A对象
时,并且A对象
覆写了finalize方法
。所以此时即使A对象已经没有任何强引用了垃圾收集器也不能立刻回收他。而是由JVM虚拟机
为A对象创建一个【终结器引用对象】 - 并且将这个【终结器引用对象】加入到引用队列
ReferenceQueue
- 这时A对象还没有被垃圾回收
- 再由一个优先级很低的线程(finalizeHandler),去查看引用队列
ReferenceQueue
中是否有新加入的元素- 如果有,则从队列中取出这个【终结器引用对象】并由这个【终结器引用对象】找到A对象,并且调用他的
finalize()方法
,等finalize方法
调用完了,这个对象就可以被垃圾收集器回收了。
- 如果有,则从队列中取出这个【终结器引用对象】并由这个【终结器引用对象】找到A对象,并且调用他的
3、原因总结
可以发现,finalize方法
工作效率很低,有以下几点
- 第一点:第一次垃圾回收时,即使这个对象已经没有了任何强引用,垃圾回收器也不能回收他,而是为他创建一个
终结器对象
并加入到队列 - 第二点:处理队列中的【终结器引用对象】的线程优先级很低,很难被CPU执行到,进而导致对象的
finalize方法
迟迟不能被执行,资源迟迟不能被释放,对象迟迟不能被垃圾回收 - 第三点:覆写
finalize方法
有可能会造成垃圾对象复活
4、覆写finalize方法造成对象复活分析
①、代码案例
@Data
public class Student {
String name;
int age;
/**
* 覆写finalize
*/
@Override
protected void finalize() throws Throwable {
// 在finalize方法中使得当前对象再次被引用(这就是对象复活)
Classroom.student = this;
}
}
@Data
public class Classroom {
// 静态变量
public static Student student;
}
②、案例分析
- 上面代码中Student类覆写了finalize方法,当student对象没有任何强引用后,便会被垃圾收集器标记为垃圾(但不能立刻回收,需要执行完
finalize方法
才能回收) - 当JVM线程执行student对象的finalize方法的时候,在该方法中该对象被静态变量重新引用
Classroom.student
- 所以当再次GC的时候,会发现本来已经是垃圾的student对象现在突然不是垃圾了(复活了)
5、参考
https://www.cnblogs.com/manayi/p/14651133.html
https://blog.csdn.net/weixin_39843151/article/details/110618282
以上是关于java为什么不推荐覆写Object的finalize方法的主要内容,如果未能解决你的问题,请参考以下文章
11.JAVA-Object类之finalize(),clone(),toString()等方法覆写