Java 中为什么要避免使用 Finalizer?

大家好,我是磊磊落落,目前我在技术上主要关注:Java、Golang、架构设计、云原生和自动化测试。欢迎来我的博客(leileiluoluo.com)获取我的最近更新!

本文原始链接:https://leileiluoluo.com/posts/avoid-using-finalizers-in-java.html

Java 中的 finalize() 方法是 Object 类自带的一个方法,因所有的类都继承自 Object,所以所有类都是 Object 的子类,我们在子类重写 finalize() 方法就可以说使用了 Finalizer,使用其的目的一般是希望做一些对象销毁前最终的资源释放操作。而上文「Java try-with-resources 特性详解」里边介绍过,针对需要释放的资源,可以通过实现 AutoClosable 接口以及结合使用 try-with-resources 特性来实现。而 Finalizer,一般仅用于原生资源(非 Java 对象,不受 JVM 管理,一般通过调用原生方法来实现对其的释放)的释放这一个场景,除此之外,都应当避免对其的使用。

本文目的即是通过介绍 Finalizer 的执行机制以及罗列其在功能、性能上的各种问题来解释为什么要避免对其的使用。

1 Finalizer 执行机制

下面简单介绍一下垃圾收集器对 Finalizer 对象的处理逻辑:当垃圾收集器检测到一个对象不可达时(不被任何线程中的任何对象所引用),若其是一个普通对象(未重写 finalize() 方法的对象),无需额外处理,进行回收即可;而若其是一个 Finalizer 对象(重写了 finalize() 方法的对象),则处理逻辑会很复杂,其首先会被 JVM 线程放进 Finalizer 队列(此时,可被该对象访问的其它对象,即便已经不可达,也都要随其暂时保留),然后在后期的某个不确定的时刻,JVM 线程再次将其从队列中取出,并调用其 finalize() 方法,完成后,其才真正可被垃圾收集器所回收。

下图演示了 Finalizer 对象 obj 自创建到销毁的整个生命周期:

(Finalizer 对象  的生命周期)

解释了垃圾收集器对 Finalizer 对象的处理逻辑,下面看一下为什么 Java 里不建议使用 Finalizer 呢?

2 Finalizer 存在的问题

下面列出 Finalizer 存在的几个问题:

  • 何时执行无法保证

    对于一些对象,可能其整个生命周期都在被引用,这样的对象就不会得到回收,所以其若重写 finalize() 方法也永远不会得到执行;当一个对象不可达后,不管其是 Finalizer 对象还是普通对象,其何时被垃圾收集器回收是不确定的,甚至一直得不到回收而抛出 OutOfMemoryError 也是有可能的。

  • 执行时出现未捕获的异常会被忽略

    通常,程序中若出现未捕获的异常,线程会因此终止并打印异常信息。但若是在 JVM 线程执行 Finalizer 对象的 finalize() 方法时出现未捕获的异常,则该异常会被忽略,finalize() 的执行也会终止,但不会打印任何信息。这样极有可能造成对象的状态异常,其它使用该对象的线程也可能出现异常行为。

  • finalize() 里边的实现可能会造成内存保留问题

    finalize() 是一个普通方法,任意代码都可以写在里边,这样也就可能会造成问题。在前面的 Finalizer 执行机制中介绍过,当垃圾收集器检测到一个 Finalizer 对象不可达时,该对象会被 JVM 线程放进 Finalizer 队列,可被该对象访问的其它对象,即便已经不可达,也都要随其暂时保留;而在后期的某个不确定的时刻,JVM 线程再次将 Finalizer 对象从队列中取出,并调用其 finalize() 方法时,该 Finalizer 对象可能会再次引用本已不可达的其它对象,这样这些被引用对象的释放就会成为问题。因 Finalizer 对象的 finalize() 方法最多仅会被 JVM 线程执行一次,而当这些被引用对象再次不可达时,finalize() 方法不会再次执行,它们也因此没有机会得到释放,从而造成内存保留问题。

综上,本文介绍了 Finalizer 的执行机制并列举了其存在的问题,结论是除了对原生资源的清理,其它情形均无需对其使用。需要补充的是:Java 9 已将 Object 类的 finalize() 方法废弃,作为替代,引入了 Cleaner,虽然 Cleaner 比 Finalizer 强一点,类的所有者对其清理线程有一定的控制权,但 Cleaner 的执行仍由垃圾收集器所控制,所以 Finalizer 具有的无法保证何时执行等问题 Cleaner 同样具有,所以 Cleaner 同样不建议使用。

参考资料

[1] Creating and Destroying Objects: Avoid finalizers and cleaners | Effective Java (3rd Edition), by Joshua Bloch

[2] How to Handle Java Finalization’s Memory-Retention Issues | Oracle - www.oracle.com

[3] Why You Shouldn’t Use Finalizers in Java | Medium - medium.com

[4] JEP 421: Deprecate Finalization for Removal | OpenJDK - openjdk.org

[5] Why do finalizers have a “severe performance penalty”? | Stackoverflow - stackoverflow.com

[6] When is the finalize() method called in Java? | Stackoverflow - stackoverflow.com

[7] Java SE 9: Deprecation of finalize method | Oracle Java Doc - docs.oracle.com

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值