我一直在阅读关于finalize()的很多新手Java问题,并发现有点令人困惑的是,没有人真正说明finalize()是一种不可靠的清理资源的方法.我看到有人评论说他们用它来清理Connections,这真的很可怕,因为接近Connection的关闭的唯一方法就是最终实现try(catch).
我没有受过CS的教育,但是我已经用Java专业编程近十年了,我从未见过有人在生产系统中实现finalize().这仍然不意味着它没有它的用途,或者我与之合作过的人一直在做正确的事.
所以我的问题是,实现finalize()的用例是什么,无法通过语言中的其他进程或语法更可靠地处理?
请提供具体的方案或您的经验,只是重复Java教科书,或最终确定的用途是不够的,这不是这个问题的意图.
您可以将它用作持有外部资源(套接字,文件等)的对象的后备.实施一个close()
需要调用方法和文档.
落实finalize()
做close()
,如果你发现它并没有这样做处理.也许有一些东西倾倒到stderr指出你在一个越野车来电后正在清理.
它在特殊/错误的情况下提供额外的安全性.并非每个来电者都会做正确的事stderr
事情.不幸的是,但在大多数环境中都是如此.
我同意这很少需要.正如评论者指出的那样,它带有GC开销.只有在长时间运行的应用程序中需要"腰带和吊带"安全时才使用.
我看到Java 9 try {} finally {}
的版本已被弃用!他们指出,我们Object.finalize()
并java.lang.ref.Cleaner
作为替代品.
finalize()是对JVM的一个暗示,它可能在未指定的时间执行您的代码.当您希望代码神秘地无法运行时,这很好.
在终结器中做任何重要的事情(基本上除了记录之外的任何事情)在三种情况下也很好
你想赌博,其他最终的对象仍将处于你的程序的其余部分认为有效的状态.
您希望向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在最终确定后正确运行.
你想要意外地复活最终的对象,并花费大量的时间来弄清楚它们为什么不起作用,和/或为什么它们最终在最终发布时没有最终确定.
如果您认为需要finalize(),有时您真正想要的是一个幻像引用(在给出的示例中,它可以保存对其参考使用的连接的硬引用,并在幻像引用排队后关闭它).它也有可能神秘地永远不会运行的属性,但至少它不能调用方法或复活最终的对象.因此,对于你并非绝对需要干净地关闭该连接的情况,这是恰到好处的,但是你非常喜欢,并且你班级的客户不能或不会自己打电话(这实际上是公平的 - 如果您设计需要在收集之前采取特定操作的接口,那么拥有垃圾收集器的重点是什么?这只会让我们回到malloc/free的时代.)
其他时候,您需要您认为自己管理的资源更强大.例如,为什么需要关闭该连接?它最终必须基于某种I/O系统提供的(套接字,文件,等等),那么你为什么不能依靠系统来为你关闭它,当资源的最低水平gced?如果另一端的服务器绝对要求您干净地关闭连接而不是仅仅丢弃套接字,那么当有人绊倒您运行代码的机器的电源线或干预网络时,会发生什么?
免责声明:我过去曾参与过JVM实施.我讨厌终结者.
一个简单的规则:永远不要使用终结器.仅对象具有终结器(无论它执行什么代码)的事实足以导致垃圾收集的相当大的开销.
来自Brian Goetz 的文章:
具有终结器的对象(具有非平凡的finalize()方法的对象)与没有终结器的对象相比具有显着的开销,并且应该谨慎使用.可完成对象的分配速度较慢,收集速度较慢.在分配时,JVM必须使用垃圾收集器注册任何可终结对象,并且(至少在HotSpot JVM实现中)可完成对象必须遵循比大多数其他对象更慢的分配路径.同样,可完成对象的收集速度也较慢.在可回收可完成对象之前,它至少需要两次垃圾收集周期(在最好的情况下),并且垃圾收集器必须做额外的工作来调用终结器.结果是花费更多的时间来分配和收集对象以及对垃圾收集器施加更多压力,因为无法访问的可终结对象使用的内存保留的时间更长.将其与终结器无法保证在任何可预测的时间范围内运行甚至根本不运行这一事实相结合,您可以看到相对较少的情况,最终确定是正确的工具.
我在生产代码中使用finalize的唯一一次是实现检查已清理给定对象的资源,如果没有,则记录一个非常有声的消息.它实际上没有尝试自己做,如果没有正确完成,它只是大声喊叫.结果证明是非常有用的.
自1998年以来,我一直从事Java专业工作,而且我从未实现过finalize().不止一次.
我使用finalize一次来了解哪些对象被释放.你可以用静力学,参考计数等玩一些整洁的游戏 - 但它只是用于分析.
接受的答案是好的,我只是想补充一点,现在有一种方法可以完成最终化的功能而根本不使用它.
查看"参考"类.弱参考等
您可以使用它们来保持对所有对象的引用,但此引用ALONE不会停止GC.关于这一点的好处是你可以让它在被删除时调用一个方法,并且可以保证调用这个方法.
另外需要注意的事项.任何时候你在代码中的任何地方看到这样的东西(不只是在最终确定,但那是你最有可能看到的地方):
public void finalize() { ref1 = null; ref2 = null; othercrap = null; }
这表明有人不知道他们在做什么.这样的"清理"几乎从不需要.当类是GC时,这是自动完成的.
如果你在最终确定中找到这样的代码,那么保证编写它的人会感到困惑.
如果它在其他地方,可能是代码是一个坏模型的有效补丁(一个类保持很长时间,并且出于某种原因,它引用的东西必须在对象被GC之前被手动释放).一般来说,这是因为有人忘了删除一个监听器或其他东西,并且无法弄清楚为什么他们的对象不是GC,所以他们只是删除它所指的东西并耸耸肩并走开.
永远不应该用它来清理"更快".
我不确定你能做些什么,但......
itsadok@laptop ~/jdk1.6.0_02/src/ $ find . -name "*.java" | xargs grep "void finalize()" | wc -l 41
所以我猜太阳发现了一些(他们认为)它应该被使用的情况.
class MyObject { Test main; public MyObject(Test t) { main = t; } protected void finalize() { main.ref = this; // let instance become reachable again System.out.println("This is finalize"); //test finalize run only once } } class Test { MyObject ref; public static void main(String[] args) { Test test = new Test(); test.ref = new MyObject(test); test.ref = null; //MyObject become unreachable?finalize will be invoked System.gc(); if (test.ref != null) System.out.println("MyObject still alive!"); } }
====================================
结果:
这是最终确定
MyObject还活着!
=====================================
因此,您可以在finalize方法中使无法访问的实例可访问.
finalize可以用于捕获资源泄漏.如果资源应该被关闭但是没有写入它没有关闭到日志文件并关闭它的事实.这样你就可以消除资源泄漏,并让自己知道它已经发生了,这样你就可以解决它.
自从1.0 alpha 3(1995)以来,我一直在用Java编程,我还没有覆盖任何事情的finalize ......
您不应该依赖finalize()来为您清理资源.如果那么垃圾收集,则finalize()将不会运行.使用它们时显式释放资源要好得多.
要突出显示上述答案中的一点:终结器将在单独的GC线程上执行.我听说过一个主要的Sun演示,其中开发人员为一些终结器添加了一个小小的睡眠,故意将其他花哨的3D演示带到了膝盖上.
最好避免,可能的例外是test-env诊断.
Eckel在Java中的思考有一个很好的部分.