为什么终结器会有“严重的性能损失”?

有效的Java说:

使用终结器会严重影响性能。

为什么使用终结器破坏对象的速度较慢?

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

但是,当你在混合中使用终结器时,GC不能简单地一次擦除整个一代。 相反,它需要弄清楚那一代中需要最终确定的所有对象,并将它们排队到实际执行终结器的线程上。 与此同时,GC无法有效地清理对象。 因此要么让它们保持活动的时间超过它们应该的时间,要么它必须延迟收集其他物体,或两者兼而有之。 另外,你有实际执行终结器的任意等待时间。

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

实际遇到一个这样的问题:

在Sun HotSpot JVM中,终结器在给定固定的低优先级的线程上处理。 在高负载应用程序中,与低优先级终结线程可以处理它们相比,可以更容易地创建所需的终结所需对象。 同时,finalization-pending对象使用的堆上的空间不可用于其他用途。 最终,您的应用程序可能会花费所有时间进行垃圾收集,因为所有可用内存都在待完成的对象中使用。

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

我刚从办公桌上拿起了我的副本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.

我建议阅读整个部分 – 它解释的事情比我在这里鹦鹉学得更好。

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

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

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

在不确切知道当前Java垃圾收集如何实现的情况下(实际上,因为那里有不同的Java实现,也有不同的GC),你可以假设如果一个对象有一个终结器,GC必须做一些额外的工作,因为这个function。

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