27

有效的 Java 说:

使用终结器会导致严重的性能损失。

为什么使用终结器销毁对象会更慢?

4

6 回答 6

23

因为垃圾收集器的工作方式。出于性能考虑,大多数 Java GC 使用复制收集器,其中短期对象被分配到“伊甸园”内存块中,当该代对象被收集时,GC 只需要复制那些仍然“活着”到更永久的存储空间,然后它可以一次擦除(释放)整个“伊甸园”内存块。这是有效的,因为大多数 Java 代码将创建数千个对象实例(盒装原语、临时数组等),其生命周期仅为几秒钟。

但是,当您混合使用终结器时,GC 不能简单地一次擦除一整代。相反,它需要找出该代中需要终结的所有对象,并将它们排队在实际执行终结器的线程上。同时,GC 也无法有效地完成对对象的清理。因此,它要么必须使它们保持比应有的寿命更长,要么必须延迟收集其他对象,或两者兼而有之。另外,您有实际执行终结器的任意等待时间。

所有这些因素加起来会导致显着的运行时损失,这就是为什么close()通常首选确定性终结(使用方法或类似方法来显式终结对象的状态)的原因。

于 2010-05-18T19:00:03.383 回答
10

实际上遇到了这样一个问题:

在 Sun HotSpot JVM 中,终结器在被赋予固定低优先级的线程上处理。在高负载应用程序中,创建需要完成的对象比低优先级完成线程处理它们的速度更快。同时,最终挂起对象使用的堆上的空间不可用于其他用途。最终,您的应用程序可能会花费所有时间进行垃圾收集,因为所有可用内存都被等待终结的对象使用。

当然,这是除了不使用 Effective Java 中描述的终结器的其他许多原因之外。

于 2010-05-18T19:06:41.443 回答
2

我刚从办公桌上拿起一本《Effective Java》,想看看他指的是什么。

如果您阅读第 2 章第 6 节,他将详细介绍各种性能影响。

You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

我建议阅读整个部分 - 它解释的东西比我在这里鹦鹉学舌要好得多。

于 2010-05-18T19:02:16.083 回答
2

如果您仔细阅读finalize()的文档,您会注意到终结器使对象能够防止被 GC 收集。

如果不存在终结器,则可以简单地删除对象并且不需要更多关注。但是如果有终结器,则需要在之后检查对象是否再次“可见”。

在不知道当前 Java 垃圾回收具体是如何实现的(实际上,因为有不同的 Java 实现,也有不同的 GC),你可以假设如果一个对象有一个终结器,GC 必须做一些额外的工作,因为的这个功能。

于 2010-05-18T19:03:43.697 回答
1

我的想法是这样的:Java 是一种垃圾收集语言,它根据自己的内部算法释放内存。每隔一段时间,GC 就会扫描堆,确定哪些对象不再被引用,然后释放内存。终结器会中断此操作并强制在 GC 循环之外释放内存,从而可能导致效率低下。我认为最佳实践是仅在绝对必要时使用终结器,例如释放文件句柄或关闭数据库连接,这应该确定地完成。

于 2010-05-18T18:57:37.813 回答
0

我能想到的一个原因是,如果您的资源都是 Java 对象而不是本机代码,那么显式内存清理是不必要的。

于 2010-05-18T18:59:15.830 回答