7

根据这篇文章,在.Net中,

终结者实际上比这更糟糕。除了它们运行迟缓(这对于许多资源来说确实是一个严重的问题)之外,它们也没有那么强大,因为它们只能执行析构函数中允许的操作的子集(例如,终结器不能可靠地使用其他对象,而析构函数可以),即使在那个子集中编写终结器也很难正确编写。并且收集可终结的对象是昂贵的:每个可终结的对象,以及从它可以到达的潜在的巨大对象图,都被提升到下一代 GC,这使得收集一些大倍数的代价更高。

这是否也适用于一般的 JVM,特别是 HotSpot?

4

4 回答 4

4

是 2004 年的明确声明:

具有终结器的对象(那些具有非平凡finalize()方法)与没有终结器的对象相比具有显着的开销,应谨慎使用。可终结的对象分配和收集都比较慢。在分配时,JVM 必须向垃圾收集器注册任何可终结对象,并且(至少在 HotSpot JVM 实现中)可终结对象必须遵循比大多数其他对象更慢的分配路径。同样,可终结对象的收集速度也较慢。至少需要两个垃圾收集周期(在最好的情况下)才能回收可终结对象,并且垃圾收集器必须做额外的工作来调用终结器。结果是分配和收集对象花费了更多时间,垃圾收集器承受了更大的压力,因为无法访问的可终结对象使用的内存保留的时间更长。

于 2010-06-02T05:14:06.707 回答
3

以下是 Effective Java 2nd Edition 中的一些精选引语:第 7 项:避免终结器:

终结器是不可预测的,通常是危险的,而且通常是不必要的。它们的使用会导致行为不稳定、性能不佳和可移植性问题。终结器几乎没有有效用途,[...] 作为经验法则,您应该避免使用终结器。

你真的应该确保你确实需要终结器;大多数时候你不这样做。

警告 C++ 程序员不要将终结器视为 Java 对 C++ 析构函数的模拟。在 C++ 中,析构函数是回收与对象关联的资源的常用方法,是构造函数的必要对应物。在 Java 中,垃圾收集器会在对象变得无法访问时回收与对象关联的存储,而程序员不需要特别努力。C++ 析构函数也用于回收其他非内存资源。在 Java 中,try-finally块通常用于此目的。

调用终结器的语义也很重要:

JLS 12.6 类实例的终结

Java 编程语言没有指定多久finalizer会调用 a [...也没有] 哪个线程将为任何给定对象调用终结器。[...]如果在终结期间抛出未捕获的异常,则忽略该异常并终止该对象的终结。(JLS 12.6.2) 终结器调用未排序

此外,按需运行终结器的唯一机制被打破。以下引用来自Effective Java 2nd Edition:

[...] 声称保证最终确定的唯一方法是System.runFinalizersOnExit及其邪恶的孪生兄弟,Runtime.runFinalizersOnExit. 这些方法存在致命缺陷,已被弃用。

Bloch 进一步评论了性能损失(强调他的):

哦,还有一件事:使用 finalizers会有严重的性能损失。在我的机器上,创建和销毁一个简单对象的时间约为 5.6ns。添加终结器会将时间增加到 2,400ns。换句话说,使用终结器创建和销毁对象的速度大约慢了 430 倍。

由于基准测试方法的细节如此之少,我认为具体数字意义不大,但它确实证实了已被广泛记录的内容:终结器非常昂贵。

这本书确实解释了使用终结器有效的罕见场景。从这个答案中省略这些引用是故意的。

于 2010-06-02T07:31:40.187 回答
1

Java 有一个非常相似的终结器机制。这是一篇关于终结器的好文章:http ://www.javaworld.com/javaworld/jw-06-1998/jw-06-techniques.html

Java 中的一般经验法则是不要使用它们。

于 2010-06-02T04:31:10.730 回答
0

finalize()当垃圾收集器确定不再有对该对象的引用时,由垃圾收集器对该对象调用。子类覆盖 finalize 方法以释放系统资源或执行其他清理。

不要使用 Finalizer,主要是因为不可预测,我们不知道什么 时候会被执行,“不要试图比 JVM 更聪明”

于 2014-01-30T18:35:51.873 回答