当前位置:  开发笔记 > 编程语言 > 正文

你为什么要实现finalize()?

如何解决《你为什么要实现finalize()?》经验,为你挑选了11个好方法。

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

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

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

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



1> John M..:

您可以将它用作持有外部资源(套接字,文件等)的对象的后备.实施一个close()需要调用方法和文档.

落实finalize()close(),如果你发现它并没有这样做处理.也许有一些东西倾倒到stderr指出你在一个越野车来电后正在清理.

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

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

我看到Java 9 try {} finally {}的版本已被弃用!他们指出,我们Object.finalize()java.lang.ref.Cleaner作为替代品.


我确实使用了finalize这个确切的原因.我继承了代码,它非常错误,并且倾向于保持数据库连接打开.我们修改了连接以包含有关创建它们的时间和位置的数据,然后实现finalize以在连接未正确关闭时记录此信息.事实证明,追踪非封闭连接非常有帮助.
只需确保finalize()永远不会抛出异常,否则垃圾收集器将不会继续清理该对象,并且您将获得内存泄漏.
不保证会终止Finalize,所以不要指望它释放资源.
skaffman - 我不相信(除了一些有缺陷的JVM实现).来自Object.finalize()javadoc:如果finalize方法抛出未捕获的异常,则忽略该异常并终止该对象的终止.
你的建议可以长时间隐藏人们的错误(不叫关闭).我不建议这样做.
我想我通常记录得很好,然后当他们不阅读文档时会对人们大喊大叫.我想这也会有所帮助.*笑容*
如果您需要"安全带和吊带"安全来保持重要系统运行,我不希望抛出AssertionError.我希望通过执行清理来最大化系统保持运行的可能性,同时将信息留在日志中以便我可以意识到出错了.
为什么不直接在finilize中检查对象是否已准备好处理,即"关闭"而不是写入日志?
Finalize损害了GC性能,因为垃圾收集器必须检查两次是否已收集对象。

2> Steve Jessop..:

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

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

你想赌博,其他最终的对象仍将处于你的程序的其余部分认为有效的状态.

您希望向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在最终确定后正确运行.

你想要意外地复活最终的对象,并花费大量的时间来弄清楚它们为什么不起作用,和/或为什么它们最终在最终发布时没有最终确定.

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

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

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


提升极端讽刺的正义.
这不回答问题; 它只是说`finalize()`的所有缺点,而没有给出任何人真正想要使用它的理由.
@SteveJessop:如果基类构造函数要求其他实体代表正在构造的对象更改其状态(一种非常常见的模式),但是派生类字段初始化器抛出异常,则部分构造的对象应立即清除它本身之后(即通知那些外部实体不再需要它们的服务),但是Java和.NET都没有提供任何甚至远程清除的模式,通过这种模式可以发生这种情况。确实,他们似乎竭尽全力以致于难以引用需要清除的内容才能到达清除代码。

3> Tom..:

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

来自Brian Goetz 的文章:

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



4> skaffman..:

我在生产代码中使用finalize的唯一一次是实现检查已清理给定对象的资源,如果没有,则记录一个非常有声的消息.它实际上没有尝试自己做,如果没有正确完成,它只是大声喊叫.结果证明是非常有用的.



5> Paul Tomblin..:

自1998年以来,我一直从事Java专业工作,而且我从未实现过finalize().不止一次.


@ InfantPro'Aravind'调用finalize不会删除该对象。这是您实现的一种方法,JVM可以在垃圾回收对象时以及何时垃圾回收对象。

6> Bill K..:

我使用finalize一次来了解哪些对象被释放.你可以用静力学,参考计数等玩一些整洁的游戏 - 但它只是用于分析.

接受的答案是好的,我只是想补充一点,现在有一种方法可以完成最终化的功能而根本不使用它.

查看"参考"类.弱参考等

您可以使用它们来保持对所有对象的引用,但此引用ALONE不会停止GC.关于这一点的好处是你可以让它在被删除时调用一个方法,并且可以保证调用这个方法.

另外需要注意的事项.任何时候你在代码中的任何地方看到这样的东西(不只是在最终确定,但那是你最有可能看到的地方):

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

这表明有人不知道他们在做什么.这样的"清理"几乎从不需要.当类是GC时,这是自动完成的.

如果你在最终确定中找到这样的代码,那么保证编写它的人会感到困惑.

如果它在其他地方,可能是代码是一个坏模型的有效补丁(一个类保持很长时间,并且出于某种原因,它引用的东西必须在对象被GC之前被手动释放).一般来说,这是因为有人忘了删除一个监听器或其他东西,并且无法弄清楚为什么他们的对象不是GC,所以他们只是删除它所指的东西并耸耸肩并走开.

永远不应该用它来清理"更快".


我认为,幻像引用是跟踪哪些对象被释放的更好方法.
提供最好的解释我曾经听过的"弱参考".

7> itsadok..:

我不确定你能做些什么,但......

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

所以我猜太阳发现了一些(他们认为)它应该被使用的情况.


但是这些用途中有多少只是实现或测试对`finalize`的支持而不是实际使用它?
我想这也是"Sun开发人员总是对的"问题吗?
@damryfbfnetsi:尝试通过http://chocolatey.org/packages/ack安装ack(http://beyondgrep.com/)。或者使用$ FAVORITE_IDE中的简单“在文件中查找”功能。

8> 小智..:
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方法中使无法访问的实例可访问.


出于某种原因,这段代码让我想起了僵尸电影.你GC你的对象,然后它再次站起来......
不,上面是错误的代码.这是为什么敲定很糟糕的一个例子.

9> TofuBeer..:

finalize可以用于捕获资源泄漏.如果资源应该被关闭但是没有写入它没有关闭到日志文件并关闭它的事实.这样你就可以消除资源泄漏,并让自己知道它已经发生了,这样你就可以解决它.

自从1.0 alpha 3(1995)以来,我一直在用Java编程,我还没有覆盖任何事情的finalize ......



10> Bill the Liz..:

您不应该依赖finalize()来为您清理资源.如果那么垃圾收集,则finalize()将不会运行.使用它们时显式释放资源要好得多.



11> Michael East..:

要突出显示上述答案中的一点:终结器将在单独的GC线程上执行.我听说过一个主要的Sun演示,其中开发人员为一些终结器添加了一个小小的睡眠,故意将其他花哨的3D演示带到了膝盖上.

最好避免,可能的例外是test-env诊断.

Eckel在Java中的思考有一个很好的部分.

推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有