你为什么要实现finalize()?

问题

我一直在阅读关于finalize()的很多新手Java问题,并发现有点令人困惑的是,没有人真正说明finalize()是一种不可靠的清理资源的方法。我看到有人评论说他们用它来清理Connections,这真的很可怕,因为接近Connection的一个保证就是最终实现try(catch)。

我没有受过CS的教育,但是我已经用Java专业编程近十年了,我从未见过有人在生产系统中实现finalize()。这仍然不意味着它没有它的用​​途,或者我与之合作的人一直在做正确的事。

所以我的问题是,实现finalize()的用例是什么,无法通过语言中的其他进程或语法更可靠地处理?

请提供具体的方案或你的经验,只是重复Java教科书,或最终确定的用途是不够的,这不是这个问题的意图。


#1 热门回答(197 赞)

你可以将它用作持有外部资源(套接字,文件等)的对象的后备。实现aclose()方法并记录需要调用的方法。

如果你检测到尚未完成,则执行finalize()进行close()处理。也许有一些东西倾倒到stderr指出你在一个越野车来电后正在清理。

它在特殊/错误的情况下提供额外的安全性。并非每个来电者都会每次都做正确的try {} finally {}。不幸的是,但在大多数环境中都是如此。

我同意这很少需要。正如评论者指出的那样,它带有GC开销。只有在长时间运行的应用程序中需要"腰带和吊带"安全时才使用。

我看到Java 9,Object.finalize()已被弃用!他们指出我们选择java.lang.ref.Cleanerjava.lang.ref.PhantomReference


#2 热门回答(149 赞)

finalize()是对JVM的一个暗示,它可能很好地在未指定的时间执行你的代码。当你希望代码神秘地无法运行时,这很好。

在终结器中做任何重要的事情(基本上除了记录之外的任何事情)在三种情况下也很好

  • 你想赌博其他已敲定的对象仍将处于程序其余部分认为有效的状态。
  • 你希望向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在最终确定后正确运行。
  • 你想要意外地复活最终的对象,并花费大量的时间来弄清楚它们为什么不起作用,和/或为什么它们最终在最终发布时没有最终确定。

如果你认为需要finalize(),有时你真正想要的是一个幻像引用(在给出的示例中,它可以保存对其参考使用的连接的硬引用,并在幻像引用排队后关闭它)。这也有可能神秘地永远不会运行的属性,但至少它不能调用方法或复活最终的对象。因此,对于你并不是绝对需要干净地关闭那个连接的情况,这是正确的,但是你非常喜欢,并且你班级的客户不能或不会自己打电话(这实际上是公平的 - 如果你设计的接口需要在收集之前采取特定的操作,那么拥有垃圾收集器有什么意义呢?这只会让我们回到malloc / free的时代。)

其他时候,你需要你认为自己管理的资源更强大。例如,为什么需要关闭该连接?它最终必须基于系统提供的某种I / O(套接字,文件等),那么为什么在最低资源水平时你不能依赖系统来关闭它?如果另一端的服务器绝对要求你干净地关闭连接而不是仅仅丢弃套接字,那么当有人绊倒你运行代码的机器的电源线或干预网络时,会发生什么?

免责声明:我过去曾参与过JVM实施。我讨厌终结者。


#3 热门回答(49 赞)

一个简单的规则:永远不要使用终结器。一个对象具有终结器(无论它执行什么代码)的事实足以导致垃圾收集的相当大的开销。

来自article的Brian Goetz:

具有终结器的对象(具有非平凡的finalize()方法的对象)与没有终结器的对象相比具有显着的开销,并且应该谨慎使用。可完成对象的分配速度较慢,收集速度较慢。在分配时,JVM必须使用垃圾收集器注册任何可终结对象,并且(至少在HotSpot JVM实现中)可完成对象必须遵循比大多数其他对象更慢的分配路径。同样,可完成对象的收集速度也较慢。在可回收可完成对象之前,它至少需要两次垃圾收集周期(在最好的情况下),并且垃圾收集器必须做额外的工作来调用终结器。结果是花费更多时间分配和收集对象以及对垃圾收集器施加更多压力,因为无法访问的可终结对象使用的内存保留的时间更长。将其与终结器无法保证在任何可预测的时间范围内运行甚至根本不运行这一事实相结合,你可以看到相对较少的情况,最终确定是正确的工具。