为什么要实现finalize()?
我阅读了很多关于 Finalize() 的 Java 新手问题,发现没有人真正明确表示 Finalize() 是一种不可靠的资源清理方法,这让我感到有点困惑。 。 我看到有人评论说他们用它来清理连接,这真的很可怕,因为接近保证连接关闭的唯一方法是最后实现 try (catch)。
我没有接受过计算机科学方面的教育,但我已经从事 Java 专业编程近十年了,而且我从未见过有人在生产系统中实现 finalize()
。 这仍然并不意味着它没有用处,也不意味着与我共事的人一直在做正确的事情。
所以我的问题是,实现 Finalize() 的用例有哪些无法通过语言中的其他进程或语法更可靠地处理?
请提供具体的场景或您的经验,简单地重复Java教科书,或最终确定的预期用途是不够的,因为不是这个问题的意图。
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.
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.
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?
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
接受的答案很好,我只是想补充一点,现在有一种方法可以在不实际使用它的情况下获得 Finalize 功能。
查看“参考”课程。 弱引用、幻像引用和弱引用 软参考。
您可以使用它们来保留对所有对象的引用,但仅此引用不会停止 GC。 这样做的巧妙之处在于,您可以让它在删除时调用一个方法,并且可以保证调用该方法。
至于最终确定:
我使用过一次 Finalize 来了解哪些对象被释放。 你可以用静态、引用计数等玩一些巧妙的游戏——但这只是为了分析,但要注意这样的代码(不仅仅是在 Finalize 中,但这是你最有可能看到它的地方)
:表明某人不知道自己在做什么。 实际上从来不需要像这样的“清理”。 当类被 GC 时,这是自动完成的。
如果你在finalize中发现这样的代码,那么编写它的人肯定会感到困惑。
如果它在其他地方,则该代码可能是对错误模型的有效补丁(一个类会保留很长时间,并且由于某种原因,在对象被 GC 之前必须手动释放它引用的东西)。 一般来说,这是因为有人忘记删除侦听器或其他东西,并且无法弄清楚为什么他们的对象没有被 GC,所以他们只是删除它引用的东西,然后耸耸肩走开。
它不应该被用来“更快”地清理东西。
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.
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 guaranteed to be called.
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):
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.
If you find code like that in a finalize it's guaranteed that the person who wrote it was confused.
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.
It should never be used to clean things up "Quicker".
我从 1998 年开始专业从事 Java 工作,但从未实现过
finalize()
。 不止一次。I've been doing Java professionally since 1998, and I've never implemented
finalize()
. Not once.我不确定你能对此做些什么,但是......
所以我猜太阳发现了一些案例(他们认为)应该使用它。
I'm not sure what you can make of this, but...
So I guess the Sun found some cases where (they think) it should be used.
删除已添加到全局/静态位置(不需要)并且需要在删除对象时删除的内容可以很方便。 例如:
It can be handy to remove things that have been added to a global/static place (out of need), and need to be removed when the object is removed. For instance:
嗯,我曾经用它来清理没有返回到现有池的对象。
它们被多次传递,因此无法判断它们何时可以安全返回泳池。 问题在于,它在垃圾收集期间带来了巨大的损失,远远大于通过对象池节省的任何费用。 它已经制作了大约一个月,然后我拆掉了整个池,使一切变得动态并完成了。
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.
当编写供其他开发人员使用的代码时,需要调用某种“清理”方法来释放资源。 有时,其他开发人员忘记调用您的清理(或关闭、销毁或其他)方法。 为了避免可能的资源泄漏,您可以检查 Finalize 方法以确保该方法被调用,如果没有被调用,您可以自己调用它。
许多数据库驱动程序在其 Statement 和 Connection 实现中执行此操作,以便为忘记调用 close 的开发人员提供一点安全性。
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.
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.
编辑:好吧,这确实行不通。 我实现了它,并认为如果它有时失败对我来说没关系,但它甚至没有调用一次 Finalize 方法。
我不是专业程序员,但在我的程序中,我认为这是使用 Finalize() 的一个很好的例子,即一个缓存,它在销毁之前将其内容写入磁盘。 因为没有必要每次销毁时都执行它,它只会加速我的程序,我希望我没有做错。
Edit: Okay, it really doesn't work. I implemented it and thought if it fails sometimes that's ok for me but it did not even call the finalize method a single time.
I am not a professional programmer but in my program I have a case that I think to be an example of a good case of using finalize(), that is a cache that writes its content to disk before it is destroyed. Because it is not necessary that it is executed every time on destruction, it does only speed up my program, I hope that it i didn't do it wrong.
请小心您在
finalize()
中所做的事情。 特别是如果您将它用于调用 close() 等操作以确保资源得到清理。 我们遇到过几种情况,将 JNI 库链接到正在运行的 Java 代码,并且在任何使用 Finalize() 调用 JNI 方法的情况下,我们都会遇到非常严重的 Java 堆损坏。 损坏不是由底层 JNI 代码本身引起的,本机库中的所有内存跟踪都很好。 事实上,我们根本就是从 Finalize() 调用 JNI 方法。这是在 JDK 1.5 中进行的,该版本仍在广泛使用。
直到很久以后我们才发现出了问题,但最终罪魁祸首始终是使用 JNI 调用的 Finalize() 方法。
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.This was with a JDK 1.5 which is still in widespread use.
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.
finalize()
对于捕获资源泄漏很有用。 如果资源应该关闭但没有将其未关闭的事实写入日志文件并关闭它。 这样您就可以消除资源泄漏,并让自己知道它已经发生,以便您可以修复它。我从 1.0 alpha 3 (1995) 就开始用 Java 编程,但我还没有重写任何东西的 Finalize...
finalize()
can be useful to catch resource leaks. If the resource should be closed but is not write the fact that it wasn't closed to a log file and close it. That way you remove the resource leak and give yourself a way to know that it has happened so you can fix it.I have been programming in Java since 1.0 alpha 3 (1995) and I have yet to override finalize for anything...
您不应该依赖 Finalize() 来清理您的资源。 Finalize() 在类被垃圾回收之前不会运行(如果那时)。 当您使用完资源后,最好显式释放它们。
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.
强调上面答案中的一点:终结器将在单独的 GC 线程上执行。 我听说过一个主要的 Sun 演示,开发人员在一些终结器中添加了一个小睡眠,并故意使原本精美的 3D 演示崩溃。
最好避免,但测试环境诊断可能除外。
Eckel 的《Java 思维》中有关于此的很好的章节。
To highlight a point in the above answers: finalizers will be executed on the lone GC thread. I have heard of a major Sun demo where the developers added a small sleep to some finalizers and intentionally brought an otherwise fancy 3D demo to its knees.
Best to avoid, with possible exception of test-env diagnostics.
Eckel's Thinking in Java has a good section on this.
====================================
结果:
============ =========================
因此,您可以在 Finalize 方法中使无法访问的实例变得可访问。
====================================
result:
=====================================
So you may make an unreachable instance reachable in finalize method.
接受的答案列出了可以在最终确定期间关闭资源。
然而 这个答案表明,至少在使用 JIT 编译器的 java8 中,您会遇到意想不到的问题,有时甚至会调用终结器在完成从对象维护的流中读取数据之前。
因此,即使在这种情况下,也不建议调用 Finalize。
The accepted answer lists that closing a resource during finalize can be done.
However this answer shows that at least in java8 with the JIT compiler, you run into unexpected issues where sometimes the finalizer is called even before you finish reading from a stream maintained by your object.
So even in that situation calling finalize would not be recommended.
一旦我们使用完资源(文件、套接字、流等),就需要关闭它们。 它们通常有
close()
方法,我们通常在try-catch
语句的finally
部分调用该方法。 有时,少数开发人员也可以使用 Finalize() ,但在我看来,这不是一个合适的方法,因为不能保证 Finalize 总是被调用。在 Java 7 中,我们有 try-with-resources 语句可以这样使用:
在上面的示例中,try-with-resource 将通过调用
close()
方法自动关闭资源BufferedReader
。 如果我们愿意,我们还可以实现 Closeable在我们自己的课程中并以类似的方式使用它。 IMO 看起来更简洁、更容易理解。The resources (File, Socket, Stream etc.) need to be closed once we are done with them. They generally have
close()
method which we generally call infinally
section oftry-catch
statements. Sometimesfinalize()
can also be used by few developers but IMO that is not a suitable way as there is no guarantee that finalize will be called always.In Java 7 we have got try-with-resources statement which can be used like:
In the above example try-with-resource will automatically close the resource
BufferedReader
by invokingclose()
method. If we want we can also implement Closeable in our own classes and use it in similar way. IMO it seems more neat and simple to understand.作为旁注:
来源:finalize() 在 java-9 上已弃用
As a side note:
Source: finalize() deprecated on java-9
iirc - 您可以使用 Finalize 方法作为实现昂贵资源池机制的一种手段 - 这样它们也不会得到 GC。
iirc - you can use finalize method as a means of implementing a pooling mechanism for expensive resources - so they don't get GC's too.
就我个人而言,我几乎从未使用过 Finalize() ,除非在一种罕见的情况下:我制作了一个自定义泛型类型集合,并编写了一个自定义的 Finalize() 方法来执行以下操作:以下:
(
CompleteObject
是我制作的一个接口,可让您指定已实现很少实现的Object
方法,例如#finalize()、
#hashCode()
和#clone()
)因此,使用姐妹
#setDestructivelyFinalizes(boolean)
方法,使用我的集合的程序可以(帮助)保证销毁对此集合的引用也会销毁对其内容的引用,并处置任何可能无意中使 JVM 保持活动状态的窗口。 我也考虑停止任何线程,但这引发了一个全新的蠕虫问题。Personally, I almost never used
finalize()
except in one rare circumstance: I made a custom generic-type collection, and I wrote a customfinalize()
method that does the following:(
CompleteObject
is an interface I made that lets you specify that you've implemented rarely-implementedObject
methods like#finalize()
,#hashCode()
, and#clone()
)So, using a sister
#setDestructivelyFinalizes(boolean)
method, the program using my collection can (help) guarantee that destroying a reference to this collection also destroys references to its contents and disposes any windows that might keep the JVM alive unintentionally. I considered also stopping any threads, but that opened a whole new can of worms.您可以将其用作持有外部资源(套接字、文件等)的对象的后盾。 实现一个
close()
方法并记录需要调用的方法。如果您检测到尚未完成,请实现
finalize()
来执行close()
处理。 也许将一些东西转储到 stderr 来指出您正在清理有问题的调用者。它在特殊/有缺陷的情况下提供额外的安全性。 并非每个调用者每次都会执行正确的
try {} finally {}
操作。 不幸的是,但在大多数环境下都是如此。我同意很少需要它。 正如评论者所指出的,它会带来 GC 开销。 仅当您在长时间运行的应用程序中需要“腰带和吊带”安全时才使用。
我看到从 Java 9 开始, < code>Object.finalize() 已弃用! 他们向我们指出
java.lang。 ref.Cleaner
和java.lang.ref.PhantomReference
作为替代方案。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.Implement
finalize()
to do theclose()
processing if you detect it hasn't been done. Maybe with something dumped tostderr
to point out that you're cleaning up after a buggy caller.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.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.
I see that as of Java 9,
Object.finalize()
is deprecated! They point us tojava.lang.ref.Cleaner
andjava.lang.ref.PhantomReference
as alternatives.finalize()
是对 JVM 的一个提示,表明在未指定的时间执行代码可能会更好。 当您希望代码神秘地无法运行时,这非常有用。在终结器中做任何重要的事情(基本上除了日志记录之外的任何事情)在三种情况下也很好:
如果您认为需要 Finalize(),有时您真正想要的是幻像引用(在给出的示例中,它可以保存对其引用对象使用的连接的硬引用,并在幻像之后关闭它)参考已排队)。 这还有一个属性,它可能神秘地永远不会运行,但至少它不能调用或复活最终对象的方法。 因此,它非常适合以下情况:您绝对不需要完全关闭该连接,但您很想这样做,并且您的班级的客户不能或不会自己调用关闭(这实际上是足够公平的 -如果您设计的接口需要在收集之前采取特定操作,那么拥有垃圾收集器有什么意义呢?这只会让我们回到 malloc/free 的时代。)
其他时候您需要您认为您正在设法变得更加强大的资源。 例如,为什么需要关闭该连接? 它最终必须基于系统提供的某种I/O(套接字、文件,等等),那么为什么不能在最低级别的资源被 gced 时依赖系统为您关闭它呢? 如果另一端的服务器绝对要求您彻底关闭连接而不是仅仅断开套接字,那么当有人被运行代码的机器的电源线绊倒或中间网络中断时会发生什么?
免责声明:我过去曾参与过 JVM 实现。 我讨厌终结器。
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.Doing anything significant in finalizers (basically anything except logging) is also good in three situations:
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 require a specific action be taken prior to collection? That just puts us back in the days of 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?
Disclaimer: I've worked on a JVM implementation in the past. I hate finalizers.
一个简单的规则:永远不要使用终结器。
仅对象具有终结器这一事实(无论它执行什么代码)就足以导致垃圾收集的相当大的开销。
来自 Brian Goetz 的文章:
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 article by Brian Goetz:
我在生产代码中使用 Finalize 的唯一一次是检查给定对象的资源是否已清理,如果没有,则记录一条非常明确的消息。 它实际上并没有尝试自己做这件事,只是如果做得不好它就会大喊大叫。 事实证明非常有用。
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.