Why would you ever implement finalize()?
我一直在阅读很多关于
我没有受过CS的教育,但是我已经用Java专业编程了近十年了,我从未见过有人在生产系统中实现
所以我的问题是,实现
请提供具体的方案或您的经验,只是重复Java教科书,或最终确定的用途是不够的,因为这不是这个问题的意图。
您可以将它用作持有外部资源(套接字,文件等)的对象的后备。实现需要调用它的
如果检测到尚未完成,则执行
它在特殊/错误的情况下提供额外的安全性。并非每个调用者都会每次都执行正确的
我同意这很少需要。正如评论者指出的那样,它带有GC开销。只有在长时间运行的应用程序中需要"腰带和吊带"安全时才使用。
我看到,从Java 9开始,
在终结器中做任何重要的事情(基本上除了记录之外的任何事情)在三种情况下也很好
- 你想赌博,其他最终的对象仍将处于你的程序的其余部分认为有效的状态。
- 您希望向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在最终确定后正确运行。
- 你想要意外地复活最终的对象,并花费大量的时间来弄清楚它们为什么不起作用,和/或为什么它们最终在最终发布时没有最终
如果您认为需要finalize(),有时您真正想要的是一个幻像引用(在给出的示例中,它可以保存对其参考使用的连接的硬引用,并在幻像引用排队后关闭它)。它也有可能神秘地永远不会运行的属性,但至少它不能调用方法或复活最终的对象。因此,对于你并非绝对需要干净地关闭该连接的情况,这是恰到好处的,但是你非常喜欢,并且你班级的客户不能或不会自己打电话(这实际上是公平的 - 如果您在收集之前设计需要采取特定操作的接口,那么拥有垃圾收集器有什么意义?这只会让我们回到malloc / free的时代。)
其他时候,您需要您认为自己管理的资源更强大。例如,为什么需要关闭该连接?它最终必须基于系统提供的某种I / O(套接字,文件,等等),那么为什么在最低资源水平时你不能依赖系统来关闭它?如果另一端的服务器绝对要求您干净地关闭连接而不是仅仅丢弃套接字,那么当有人绊倒您的代码运行的机器的电源线或干预网络时会发生什么?
免责声明:我过去曾参与过JVM实施。我讨厌终结者。
一个简单的规则:永远不要使用终结器。
仅对象具有终结器(无论它执行什么代码)的事实足以导致垃圾收集的相当大的开销。
来自Brian Goetz的文章:
Objects with finalizers (those that
have a non-trivial finalize() method)
have significant overhead compared to
objects without finalizers, and should
be used sparingly. Finalizeable
objects are both slower to allocate
and slower to collect. At allocation
time, the JVM must register any
finalizeable objects with the garbage
collector, and (at least in the
HotSpot JVM implementation)
finalizeable objects must follow a
slower allocation path than most other
objects. Similarly, finalizeable
objects are slower to collect, too. It
takes at least two garbage collection
cycles (in the best case) before a
finalizeable object can be reclaimed,
and the garbage collector has to do
extra work to invoke the finalizer.
The result is more time spent
allocating and collecting objects and
more pressure on the garbage
collector, because the memory used by
unreachable finalizeable objects is
retained longer. Combine that with the
fact that finalizers are not
guaranteed to run in any predictable
timeframe, or even at all, and you can
see that there are relatively few
situations for which finalization is
the right tool to use.
我在生产代码中使用finalize的唯一一次是实现检查已清理给定对象的资源,如果没有,则记录一个非常有声的消息。它实际上没有尝试自己做,如果没有正确完成,它只是大声喊叫。结果证明是非常有用的。
自1998年以来,我一直从事Java专业工作,而且我从未实现
接受的答案是好的,我只是想补充一点,现在有一种方法可以完成最终化的功能而根本不使用它。
查看"参考"类。弱参考,幻影参考&软参考。
您可以使用它们来保留对所有对象的引用,但此引用ALONE不会停止GC。关于这一点的好处是你可以让它在被删除时调用一个方法,并且可以保证调用这个方法。
至于完成:
我使用finalize一次来了解哪些对象被释放。你可以用静态,引用计数等来玩一些整洁的游戏 - 但它只是用于分析,但要注意这样的代码(不仅仅是最终确定,而是你最有可能看到它的地方):
1 2 3 4 5 | public void finalize() { ref1 = null; ref2 = null; othercrap = null; } |
这表明有人不知道他们在做什么。这样的"清理"几乎从不需要。当类是GC时,这是自动完成的。
如果你在最终确定中找到这样的代码,那么保证编写它的人会感到困惑。
如果它在其他地方,可能是代码是一个坏模型的有效补丁(一个类保持很长时间,并且出于某种原因,它引用的东西必须在对象被GC之前被手动释放)。一般来说,这是因为有人忘了删除一个监听器或其他东西,并且无法弄清楚为什么他们的对象不是GC,所以他们只是删除它所指的东西并耸耸肩并走开。
永远不应该用它来清理"更快"。
我不确定你能做些什么,但......
1 2 3 | itsadok@laptop ~/jdk1.6.0_02/src/ $ find . -name"*.java" | xargs grep"void finalize()" | wc -l 41 |
所以我猜太阳发现了一些(他们认为)它应该被使用的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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!"); } } |
====================================
结果:
1 2 3 | This is finalize MyObject still alive! |
=====================================
因此,您可以在finalize方法中使无法访问的实例可访问。
自从1.0 alpha 3(1995)以来,我一直在用Java编程,我还没有覆盖任何事情的finalize ......
您不应该依赖finalize()来为您清理资源。如果那么垃圾收集,则finalize()将不会运行。使用它们时显式释放资源要好得多。
要突出显示上述答案中的一点:终结器将在单独的GC线程上执行。我听说过一个主要的Sun演示,其中开发人员给一些终结器添加了一个小小的睡眠,故意将其他花哨的3D演示带到了膝盖上。
最好避免,可能的例外是test-env诊断。
Eckel在Java中的思考有一个很好的部分。
嗯,我曾经用它来清理没有返回到现有池的对象。
他们被传递了很多,因此无法确定何时可以安全返回游泳池。问题是它在垃圾收集过程中引入了一个巨大的惩罚,远远超过汇集对象的任何节省。在我撕掉整个游泳池之前,它已经生产了大约一个月,使一切变得充满活力并完成了它。
小心你在
这是JDK 1.5,仍然广泛使用。
直到很久以后我们才会发现出现问题,但最终罪魁祸首总是使用JNI调用的finalize()方法。
在编写将被其他开发人员使用的代码时,需要使用某种"清理"方法来调用以释放资源。有时,其他开发人员忘记调用您的清理(或关闭,或破坏,或其他)方法。为了避免可能的资源泄漏,您可以检查finalize方法以确保调用该方法,如果不是,您可以自己调用它。
许多数据库驱动程序在其Statement和Connection实现中执行此操作,以便为忘记调用它们的开发人员提供一点安全性。
编辑:好的,它真的不起作用。我实现了它,并认为如果它失败有时对我来说没问题,但它甚至没有一次调用finalize方法。
我不是一个专业的程序员,但在我的程序中我有一个案例,我认为这是一个使用finalize()的好例子的例子,这是一个在销毁之前将其内容写入磁盘的缓存。因为没有必要在每次破坏时执行它,它只会加速我的程序,我希望我没有做错。
1 2 3 4 5 6 7 8 9 10 11 | @Override public void finalize() { try {saveCache();} catch (Exception e) {e.printStackTrace();} } public void saveCache() throws FileNotFoundException, IOException { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp")); out.writeObject(cache); } |
删除已添加到全局/静态位置(不需要)的内容非常方便,并且在删除对象时需要将其删除。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private void addGlobalClickListener() { weakAwtEventListener = new WeakAWTEventListener(this); Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK); } @Override protected void finalize() throws Throwable { super.finalize(); if(weakAwtEventListener != null) { Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener); } } |
作为旁注:
An object that overrides finalize() is treated specially by the garbage collector. Usually, an object is immediately destroyed during the collection cycle after the object is no longer in scope. However, finalizable objects are instead moved to a queue, where separate finalization threads will drain the queue and run the finalize() method on each object. Once the finalize() method terminates, the object will at last be ready for garbage collection in the next cycle.
来源:在java-9上不推荐使用finalize()
iirc - 您可以使用finalize方法作为实现昂贵资源的池化机制的一种方法 - 因此它们也不会获得GC。
接受的答案列出了在完成期间关闭资源的过程。
但是这个答案表明,至少在使用JIT编译器的java8中,您遇到了意外问题,即使在您从对象维护的流中读取之前,有时甚至会调用终结器。
因此即使在那种情况下也不建议调用finalize。
就个人而言,我几乎从不使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public void finalize() throws Throwable { super.finalize(); if (destructiveFinalize) { T item; for (int i = 0, l = length(); i < l; i++) { item = get(i); if (item == null) { continue; } if (item instanceof Window) { ((Window) get(i)).dispose(); } if (item instanceof CompleteObject) { ((CompleteObject) get(i)).finalize(); } set(i, null); } } } |
(
所以,使用一个sister
完成后,资源(文件,套接字,流等)需要关闭。它们通常具有
在Java 7中,我们有try-with-resources语句,可以像以下一样使用:
1 2 3 4 5 6 7 | try (BufferedReader br = new BufferedReader(new FileReader(path))) { // Processing and other logic here. } catch (Exception e) { // log exception } finally { // Just in case we need to do some stuff here. } |
在上面的示例中,try-with-resource将通过调用