Java 你为什么要实现 finalize()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/158174/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why would you ever implement finalize()?
提问by Spencer Kormos
I've been reading through a lot of the rookie Java questions on finalize()
and find it kind of bewildering that no one has really made it plain that finalize() is an unreliable way to clean up resources. I saw someone comment that they use it to clean up Connections, which is really scary since the only way to come as close to a guarantee that a Connection is closed is to implement try (catch) finally.
我已经阅读了很多关于 Java 新手的问题finalize()
,发现没有人真正明确 finalize() 是一种不可靠的资源清理方法,这有点令人困惑。我看到有人评论说他们用它来清理连接,这真的很可怕,因为接近保证连接关闭的唯一方法是最后实现 try (catch)。
I was not schooled in CS, but I have been programming in Java professionally for close to a decade now and I have never seen anyone implement finalize()
in a production system ever. This still doesn't mean that it doesn't have its uses, or that people I've worked with have been doing it right.
我没有受过 CS 的教育,但我已经专业用 Java 编程近十年了,我从未见过有人finalize()
在生产系统中实现过。这仍然并不意味着它没有用途,或者与我共事的人一直在做正确的事情。
So my question is, what use cases are there for implementing finalize()
that cannot be handled more reliably via another process or syntax within the language?
所以我的问题是,有哪些用例finalize()
无法通过语言中的其他进程或语法更可靠地处理?
Please provide specific scenarios or your experience, simply repeating a Java text book, or finalize's intended use is not enough, as is not the intent of this question.
请提供具体的场景或您的经验,简单地重复Java教科书,或完成预期用途是不够的,这不是本问题的意图。
采纳答案by John M
You could use it as a backstop for an object holding an external resource (socket, file, etc). Implement a close()
method and document that it needs to be called.
您可以将其用作持有外部资源(套接字、文件等)的对象的支持。实现一个close()
方法并记录它需要被调用。
Implement finalize()
to do the close()
processing if you detect it hasn't been done. Maybe with something dumped to stderr
to point out that you're cleaning up after a buggy caller.
如果您检测到它尚未完成,则实施finalize()
以进行close()
处理。也许用倾倒的东西stderr
来指出你正在清理一个有问题的来电者。
It provides extra safety in an exceptional/buggy situation. Not every caller is going to do the correct try {} finally {}
stuff every time. Unfortunate, but true in most environments.
它在异常/有问题的情况下提供额外的安全性。并非每个调用者每次都会做正确的try {} finally {}
事情。不幸的是,但在大多数环境中都是如此。
I agree that it's rarely needed. And as commenters point out, it comes with GC overhead. Only use if you need that "belt and suspenders" safety in a long-running app.
我同意它很少需要。正如评论者指出的那样,它带有 GC 开销。仅在长时间运行的应用程序中需要“腰带和吊带”安全时才使用。
I see that as of Java 9, Object.finalize()
is deprecated! They point us to java.lang.ref.Cleaner
and java.lang.ref.PhantomReference
as alternatives.
我看到从 Java 9 开始,Object.finalize()
已弃用!他们向我们指出java.lang.ref.Cleaner
并java.lang.ref.PhantomReference
作为替代方案。
回答by Paul Tomblin
I've been doing Java professionally since 1998, and I've never implemented finalize()
. Not once.
自 1998 年以来,我一直从事专业的 Java 开发,但从未实现过finalize()
. 不止一次。
回答by John Meagher
When writing code that will be used by other developers that requires some sort of "cleanup" method to be called to free up resources. Sometimes those other developers forget to call your cleanup (or close, or destroy, or whatever) method. To avoid possible resource leaks you can check in the finalize method to ensure that the method was called and if it wasn't you can call it yourself.
编写其他开发人员将使用的代码时,需要调用某种“清理”方法来释放资源。有时,其他开发人员忘记调用您的清理(或关闭、销毁或其他)方法。为了避免可能的资源泄漏,您可以检查 finalize 方法以确保该方法被调用,如果不是,您可以自己调用它。
Many database drivers do this in their Statement and Connection implementations to provide a little safety against developers who forget to call close on them.
许多数据库驱动程序在它们的 Statement 和 Connection 实现中都这样做,以防止忘记调用 close 的开发人员。
回答by Bill the Lizard
You shouldn't depend on finalize() to clean up your resources for you. finalize() won't run until the class is garbage collected, if then. It's much better to explicitly free resources when you're done using them.
您不应该依赖 finalize() 来为您清理资源。finalize() 在类被垃圾收集之前不会运行,如果那样的话。使用完资源后,明确释放资源会好得多。
回答by skaffman
The only time I've used finalize in production code was to implement a check that a given object's resources had been cleaned up, and if not, then log a very vocal message. It didn't actually try and do it itself, it just shouted a lot if it wasn't done properly. Turned out to be quite useful.
我在生产代码中使用 finalize 的唯一一次是检查给定对象的资源是否已被清理,如果没有,则记录一条非常明确的消息。它实际上并没有尝试自己做,如果做得不好,它只会大声喊叫。结果证明非常有用。
回答by Steven M. Cherry
Be careful about what you do in a finalize()
. Especially if you are using it for things like calling close() to ensure that resources are cleaned up. We ran into several situations where we had JNI libraries linked in to the running java code, and in any circumstances where we used finalize() to invoke JNI methods, we would get very bad java heap corruption. The corruption was not caused by the underlying JNI code itself, all of the memory traces were fine in the native libraries. It was just the fact that we were calling JNI methods from the finalize() at all.
小心你在一个finalize()
. 特别是如果您将它用于诸如调用 close() 之类的事情以确保清理资源。我们遇到了几种情况,其中 JNI 库链接到正在运行的 Java 代码,并且在我们使用 finalize() 调用 JNI 方法的任何情况下,我们都会得到非常严重的 Java 堆损坏。损坏不是由底层 JNI 代码本身引起的,所有内存跟踪在本机库中都很好。事实上,我们从 finalize() 调用 JNI 方法。
This was with a JDK 1.5 which is still in widespread use.
这是使用仍在广泛使用的 JDK 1.5 中的。
We wouldn't find out that something went wrong until much later, but in the end the culprit was always the finalize() method making use of JNI calls.
直到很久以后我们才发现出了什么问题,但最终罪魁祸首始终是使用 JNI 调用的 finalize() 方法。
回答by Steve Jessop
finalize()
is a hint to the JVM that it might be nice to execute your code at an unspecified time. This is good when you want code to mysteriously fail to run.
finalize()
是对 JVM 的一个提示,即在未指定的时间执行您的代码可能会很好。当您希望代码神秘地无法运行时,这很好。
Doing anything significant in finalizers (basically anything except logging) is also good in three situations:
在终结器中做任何重要的事情(基本上除了日志记录之外的任何事情)在三种情况下也很好:
- you want to gamble that other finalized objects will still be in a state that the rest of your program considers valid.
- you want to add lots of checking code to all the methods of all your classes that have a finalizer, to make sure they behave correctly after finalization.
- you want to accidentally resurrect finalized objects, and spend a lot of time trying to figure out why they don't work, and/or why they don't get finalized when they are eventually released.
- 您想赌其他最终确定的对象仍将处于程序的其余部分认为有效的状态。
- 您想向所有具有终结器的类的所有方法添加大量检查代码,以确保它们在终结后正确运行。
- 您想意外地复活最终确定的对象,并花费大量时间试图弄清楚为什么它们不起作用,和/或为什么它们在最终发布时没有得到最终确定。
If you think you need finalize(), sometimes what you really want is a phantom reference(which in the example given could hold a hard reference to a connection used by its referand, and close it after the phantom reference has been queued). This also has the property that it may mysteriously never run, but at least it can't call methods on or resurrect finalized objects. So it's just right for situations where you don't absolutely need to close that connection cleanly, but you'd quite like to, and the clients of your class can't or won't call close themselves (which is actually fair enough - what's the point of having a garbage collector at all if you design interfaces that requirea specific action be taken prior to collection? That just puts us back in the days of malloc/free.)
如果您认为需要 finalize(),有时您真正想要的是幻像引用(在给出的示例中,它可以保存对其引用所使用的连接的硬引用,并在幻像引用排队后关闭它)。这也有一个属性,它可能神秘地永远不会运行,但至少它不能调用方法或复活最终对象。因此,它适用于您并不绝对需要干净地关闭该连接,但您很想关闭该连接的情况,并且您班级的客户不能或不会自己调用 close(这实际上足够公平-什么' 如果您设计的接口要求在收集之前采取特定操作,那么拥有垃圾收集器的意义何在?这让我们回到了 malloc/free 的时代。)
Other times you need the resource you think you're managing to be more robust. For example, why do you need to close that connection? It must ultimately be based on some kind of I/O provided by the system (socket, file, whatever), so why can't you rely on the system to close it for you when the lowest level of resource is gced? If the server at the other end absolutely requires you to close the connection cleanly rather than just dropping the socket, then what's going to happen when someone trips over the power cable of the machine your code is running on, or the intervening network goes out?
其他时候,您需要您认为自己管理的资源更加强大。例如,为什么需要关闭该连接?它最终必须基于系统提供的某种 I/O(套接字、文件等),那么当最低级别的资源被 gced 时,为什么不能依靠系统为你关闭它呢?如果另一端的服务器绝对要求您干净地关闭连接而不是仅仅丢弃套接字,那么当有人绊倒正在运行您的代码的机器的电源线或中间网络断开时会发生什么?
Disclaimer: I've worked on a JVM implementation in the past. I hate finalizers.
免责声明:我过去曾研究过 JVM 实现。我讨厌终结者。
回答by Steve Jessop
Hmmm, I once used it to clean up objects that weren't being returned to an existing pool.
嗯,我曾经用它来清理没有返回到现有池中的对象。
They were passed around a lot, so it was impossible to tell when they could safely be returned to the pool. The problem was that it introduced a huge penalty during garbage collection that was far greater than any savings from pooling the objects. It was in production for about a month before I ripped out the whole pool, made everything dynamic and was done with it.
他们被传了很多次,所以不知道什么时候可以安全地回到游泳池。问题是它在垃圾收集期间引入了巨大的惩罚,远远大于池化对象所节省的任何成本。在我拆掉整个游泳池之前,它已经投入生产了大约一个月,使一切变得动态并完成了它。
回答by Bill K
The accepted answer is good, I just wanted to add that there is now a way to have the functionality of finalize without actually using it at all.
接受的答案很好,我只是想补充一点,现在有一种方法可以在不实际使用的情况下实现 finalize 功能。
Look at the "Reference" classes. Weak reference, Phantom Reference & Soft Reference.
查看“参考”类。弱参考、幻影参考和软参考。
You can use them to keep a reference to all your objects, but this reference ALONE will not stop GC. The neat thing about this is you can have it call a method when it will be deleted, and this method can be guaranteedto be called.
您可以使用它们来保持对所有对象的引用,但此引用 ALONE 不会停止 GC。这样做的好处是你可以让它在它被删除时调用一个方法,并且这个方法可以保证被调用。
As for finalize: I used finalize once to understand what objects were being freed. You can play some neat games with statics, reference counting and such--but it was only for analysis, but watch out for code like this (not just in finalize, but that's where you are most likely to see it):
至于finalize:我曾经用finalize 来了解哪些对象被释放了。您可以使用静态、引用计数等来玩一些巧妙的游戏——但它仅用于分析,但要注意这样的代码(不仅在 finalize 中,而且这是您最有可能看到它的地方):
public void finalize() {
ref1 = null;
ref2 = null;
othercrap = null;
}
It is a sign that somebody didn't know what they were doing. "Cleaning up" like this is virtually never needed. When the class is GC'd, this is done automatically.
这表明有人不知道他们在做什么。几乎不需要像这样“清理”。当类被 GC 处理时,这是自动完成的。
If you find code like that in a finalize it's guaranteed that the person who wrote it was confused.
如果您在 finalize 中发现这样的代码,那么编写它的人肯定会感到困惑。
If it's elsewhere, it could be that the code is a valid patch to a bad model (a class stays around for a long time and for some reason things it referenced had to be manually freed before the object is GC'd). Generally it's because someone forgot to remove a listener or something and can't figure out why their object isn't being GC'd so they just delete things it refers to and shrug their shoulders and walk away.
如果它在其他地方,则可能是该代码是一个错误模型的有效补丁(一个类存在很长时间,并且出于某种原因,它引用的东西必须在对象被 GC 之前手动释放)。通常这是因为有人忘记删除监听器或其他东西,并且无法弄清楚为什么他们的对象没有被 GC 处理,所以他们只是删除它所指的东西,然后耸耸肩走开。
It should never be used to clean things up "Quicker".
它永远不应该用于清理“更快”的东西。
回答by Tom
A simple rule: never use finalizers.
一个简单的规则:永远不要使用终结器。
The fact alone that an object has a finalizer (regardless what code it executes) is enough to cause considerable overhead for garbage collection.
仅对象具有终结器(无论它执行什么代码)这一事实就足以导致垃圾收集的大量开销。
From an articleby Brian Goetz:
来自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() 方法的对象)具有显着的开销,应谨慎使用。可终结对象的分配和收集都较慢。在分配时,JVM 必须向垃圾收集器注册任何可终结对象,并且(至少在 HotSpot JVM 实现中)可终结对象必须遵循比大多数其他对象更慢的分配路径。同样,可终结对象的收集速度也较慢。在回收可终结对象之前,至少需要两个垃圾收集周期(在最好的情况下),并且垃圾收集器必须做额外的工作来调用终结器。结果是分配和收集对象花费的时间更多,垃圾收集器的压力也更大,因为无法访问的可终结对象使用的内存保留的时间更长。再加上不能保证终结器在任何可预测的时间范围内运行,甚至根本不会运行,你可以看到,在相对较少的情况下,终结器是适合使用的工具。