为什么要实现finalize()?

时间:2022-12-09 12:03:35

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.

我已经阅读了很多关于finalize()的新手Java问题,发现没有人真正明确地指出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 provided specific scenarios or your experience, simply repeating a Java text book, or finalize's intended use is not enough, and is not the intent of this question.

请提供特定的场景或您的经验,简单地重复Java教科书,或最后确定预期的用途是不够的,并且不是这个问题的目的。

21 个解决方案

#1


196  

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()。也许是把一些东西扔给stderr,告诉他你正在清理一个有bug的来电者。

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中,objec .finalize()已经过时了!他们把我们指向java.lang.ref。清洁和java.lang.ref。PhantomReference作为替代品。

#2


148  

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 require a specific action be taken prior to collection? That just puts us back in the days of malloc/free.)

如果您认为需要finalize(),那么有时您真正想要的是一个phantom引用(在本例中,它可以保存对其引用所使用的连接的硬引用,并在phantom引用排队之后关闭它)。它还具有可能神秘地永远不会运行的属性,但至少它不能调用已完成对象上的方法或恢复已完成对象。这只是对你绝对不需要关闭的情况下,连接干净,但是你很想,和你的客户类不能或不会调用关闭自己(这实际上是很好——有什么意义的垃圾收集器在所有如果你设计接口,需要一个特定的行动之前采取收集?这让我们回到了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实现上工作过。我讨厌终结器。

#3


49  

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:

来自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必须向垃圾收集器注册任何finalizeable对象,并且(至少在HotSpot JVM实现中)finalizeable对象必须遵循比大多数其他对象更慢的分配路径。同样,finalizeable对象的收集速度也较慢。在回收可finalizeable对象之前,至少需要两个垃圾收集周期(在最好的情况下),并且垃圾收集器必须做额外的工作来调用终结器。结果是花更多的时间来分配和收集对象,并对垃圾收集器施加更大的压力,因为不可到达的可终结对象使用的内存被保留得更久。结合这一事实,终结器不能保证在任何可预测的时间范围内运行,甚至在所有情况下都不能运行,您可以看到,只有相对较少的情况下,最终确定是正确的使用工具。

#4


43  

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是实现检查,检查给定对象的资源是否已被清理,如果没有,则记录一个非常明确的消息。它实际上并没有自己尝试去做,它只是大叫了很多,如果做得不好的话。结果证明是非常有用的。

#5


32  

I've been doing Java professionally since 1998, and I've never implemented finalize(). Not once.

自1998年以来,我一直在从事Java专业工作,而且从未实现过finalize()。一次也没有。

#6


24  

I'm not sure what you can make of this, but...

我不知道你能做什么,但是……

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

So I guess the Sun found some cases where (they think) it should be used.

所以我猜太阳发现了一些应该使用它的情况。

#7


24  

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.

我曾经使用finalize来理解什么对象被释放。你可以用静力学、引用计数等方法玩一些简单的游戏——但这只是为了分析。

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, etc.

查看“引用”类。弱引用等。

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.

您可以使用它们来保存对所有对象的引用,但是这个引用本身不会停止GC。这个方法的好处是,当它被删除时,你可以让它调用一个方法,并且这个方法可以保证被调用。

Another thing to watch out for. Any time you see anything like this anywhere in code (not just in finalize, but that's where you are most likely to see it):

还有一件事要注意。当您在代码中看到任何类似的内容时(不仅是在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.

如果你在定稿中找到了这样的代码,就可以保证写代码的人是困惑的。

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".

它不应该被用来“更快地”清理东西。

#8


20  

class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

result:

结果:

This is finalize

这是确定

MyObject still alive!

MyObject还活着!

=====================================

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

So you may make an unreachable instance reachable in finalize method.

因此,您可以在finalize方法中使一个不可访问的实例成为可访问的。

#9


8  

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...

自1.0 alpha 3(1995)以来,我一直在用Java编程,我还没有覆盖finalize。

#10


6  

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()不会运行。当您使用完资源时,最好显式地释放它们。

#11


5  

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.

为了突出上面答案中的一点:终结器将在单个GC线程上执行。我听说过一个主要的Sun演示,开发者在一些终结器上添加了一个小睡眠,并故意在它的膝盖上添加了一个奇特的3D演示。

Best to avoid, with possible exception of test-env diagnostics.

最好避免这种情况,测试-env诊断除外。

Eckel's Thinking in Java has a good section on this.

Eckel的Java思维在这方面有一个很好的章节。

#12


2  

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.

许多数据库驱动程序在它们的语句和连接实现中这样做,以对忘记调用它们的开发人员提供一点安全性。

#13


2  

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.

嗯,我曾经使用它来清理没有返回到现有池的对象。他们被传了很多次,所以不可能知道他们什么时候可以安全地回到游泳池。问题是,它在垃圾收集过程中带来了巨大的损失,远远超过了对对象池的节省。它在生产过程中大约一个月后,我把整个水池都挖了出来,让所有的东西都充满活力,然后我就用它完成了。

#14


2  

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.

编辑:好吧,真的不行。我实现了它,并认为如果它失败了,有时这对我来说是可以的,但它甚至没有一次调用finalize方法。

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()的一个很好的例子,finalize()是在磁盘被破坏之前将其内容写到磁盘上的缓存。因为没有必要每次销毁时都执行它,它只会加速我的程序,我希望我没有做错。

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

#15


2  

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:

删除添加到全局/静态位置(出于需要)的东西是很方便的,并且在删除对象时需要删除这些东西。例如:

    private void addGlobalClickListener() {
        weakAwtEventListener = new WeakAWTEventListener(this);

        Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        if(weakAwtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
        }
    }

#16


1  

Be careful 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()方法。

#17


0  

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.

iirc——您可以使用finalize方法作为为昂贵的资源实现池机制的一种手段——这样它们就不会得到GC。

#18


0  

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.

然而,这个答案表明,至少在使用JIT编译器的java8中,您遇到了意想不到的问题,有时甚至在您从对象维护的流中读取之前就调用终结器。

So even in that situation calling finalize would not be recommended.

因此,即使在这种情况下,调用finalize也不可取。

#19


0  

Personally, I almost never use finalize() except in one rare circumstance: I made a custom generic-type collection, and I wrote a custom finalize() method that does the following:

就我个人而言,我几乎从不使用finalize(),除非是在一种罕见的情况下:我创建了一个定制的泛型类型集合,并编写了一个定制的finalize()方法,它的作用如下:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

(CompleteObject is an interface I made that lets you specify that you've implemented rarely-implemented Object methods like #finalize(), #hashCode(), and #clone())

(CompleteObject是我创建的一个接口,它允许您指定您实现了很少实现的对象方法,如#finalize()、#hashCode()和#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.

因此,使用姐妹# setdecontivelyfinalize (boolean)方法,使用我的集合的程序可以(帮助)保证销毁对该集合的引用也会破坏对其内容的引用,并处理任何可能让JVM在无意中存活的窗口。我还考虑过停止任何线程,但这打开了一个全新的蠕虫。

#20


0  

The resources (File, Socket, Stream etc.) need to be closed once we are done with their usage. They generally have close() method which we generally call in finally section of try-catch statements. Sometimes finalize() 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.

资源(文件、套接字、流等)在使用完毕后需要关闭。它们通常有close()方法,我们通常在try-catch语句的最后部分调用。有时finalize()也可以被少数开发人员使用,但在IMO上,这不是一种合适的方式,因为无法保证finalize总是被调用。

In Java 7 we have got try-with-resources statement which can be used like:

在Java 7中,我们有try-with-resources语句,可以使用如下内容:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

In the above example try-with-resource will automatically close the resource BufferedReader by invoking close() 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.

在上面的示例中,try-with-resource将通过调用close()方法自动关闭资源缓冲阅读器。如果需要,我们还可以在自己的类中实现close,并以类似的方式使用它。在我看来,理解起来更简洁。

#21


0  

As a side note:

边注:

An object that overrides finalize() is treated specially by the garbage collector. Usually, an object is immediately destroyed during the collection cycle after the object is no longer in scope. However, finalizable objects are instead moved to a queue, where separate finalization threads will drain the queue and run the finalize() method on each object. Once the finalize() method terminates, the object will at last be ready for garbage collection in the next cycle.

重写finalize()的对象由垃圾收集器专门处理。通常,在对象不在范围内之后,对象在收集周期中立即被销毁。然而,可终结的对象被转移到一个队列中,在这个队列中,单独的finalization线程将耗尽队列并在每个对象上运行finalize()方法。一旦finalize()方法终止,对象最终将在下一个循环中为垃圾收集做好准备。

finalize deprecated on java-9

完成在java-9弃用

#1


196  

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()。也许是把一些东西扔给stderr,告诉他你正在清理一个有bug的来电者。

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中,objec .finalize()已经过时了!他们把我们指向java.lang.ref。清洁和java.lang.ref。PhantomReference作为替代品。

#2


148  

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 require a specific action be taken prior to collection? That just puts us back in the days of malloc/free.)

如果您认为需要finalize(),那么有时您真正想要的是一个phantom引用(在本例中,它可以保存对其引用所使用的连接的硬引用,并在phantom引用排队之后关闭它)。它还具有可能神秘地永远不会运行的属性,但至少它不能调用已完成对象上的方法或恢复已完成对象。这只是对你绝对不需要关闭的情况下,连接干净,但是你很想,和你的客户类不能或不会调用关闭自己(这实际上是很好——有什么意义的垃圾收集器在所有如果你设计接口,需要一个特定的行动之前采取收集?这让我们回到了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实现上工作过。我讨厌终结器。

#3


49  

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:

来自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必须向垃圾收集器注册任何finalizeable对象,并且(至少在HotSpot JVM实现中)finalizeable对象必须遵循比大多数其他对象更慢的分配路径。同样,finalizeable对象的收集速度也较慢。在回收可finalizeable对象之前,至少需要两个垃圾收集周期(在最好的情况下),并且垃圾收集器必须做额外的工作来调用终结器。结果是花更多的时间来分配和收集对象,并对垃圾收集器施加更大的压力,因为不可到达的可终结对象使用的内存被保留得更久。结合这一事实,终结器不能保证在任何可预测的时间范围内运行,甚至在所有情况下都不能运行,您可以看到,只有相对较少的情况下,最终确定是正确的使用工具。

#4


43  

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是实现检查,检查给定对象的资源是否已被清理,如果没有,则记录一个非常明确的消息。它实际上并没有自己尝试去做,它只是大叫了很多,如果做得不好的话。结果证明是非常有用的。

#5


32  

I've been doing Java professionally since 1998, and I've never implemented finalize(). Not once.

自1998年以来,我一直在从事Java专业工作,而且从未实现过finalize()。一次也没有。

#6


24  

I'm not sure what you can make of this, but...

我不知道你能做什么,但是……

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

So I guess the Sun found some cases where (they think) it should be used.

所以我猜太阳发现了一些应该使用它的情况。

#7


24  

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.

我曾经使用finalize来理解什么对象被释放。你可以用静力学、引用计数等方法玩一些简单的游戏——但这只是为了分析。

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, etc.

查看“引用”类。弱引用等。

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.

您可以使用它们来保存对所有对象的引用,但是这个引用本身不会停止GC。这个方法的好处是,当它被删除时,你可以让它调用一个方法,并且这个方法可以保证被调用。

Another thing to watch out for. Any time you see anything like this anywhere in code (not just in finalize, but that's where you are most likely to see it):

还有一件事要注意。当您在代码中看到任何类似的内容时(不仅是在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.

如果你在定稿中找到了这样的代码,就可以保证写代码的人是困惑的。

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".

它不应该被用来“更快地”清理东西。

#8


20  

class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

result:

结果:

This is finalize

这是确定

MyObject still alive!

MyObject还活着!

=====================================

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

So you may make an unreachable instance reachable in finalize method.

因此,您可以在finalize方法中使一个不可访问的实例成为可访问的。

#9


8  

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...

自1.0 alpha 3(1995)以来,我一直在用Java编程,我还没有覆盖finalize。

#10


6  

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()不会运行。当您使用完资源时,最好显式地释放它们。

#11


5  

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.

为了突出上面答案中的一点:终结器将在单个GC线程上执行。我听说过一个主要的Sun演示,开发者在一些终结器上添加了一个小睡眠,并故意在它的膝盖上添加了一个奇特的3D演示。

Best to avoid, with possible exception of test-env diagnostics.

最好避免这种情况,测试-env诊断除外。

Eckel's Thinking in Java has a good section on this.

Eckel的Java思维在这方面有一个很好的章节。

#12


2  

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.

许多数据库驱动程序在它们的语句和连接实现中这样做,以对忘记调用它们的开发人员提供一点安全性。

#13


2  

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.

嗯,我曾经使用它来清理没有返回到现有池的对象。他们被传了很多次,所以不可能知道他们什么时候可以安全地回到游泳池。问题是,它在垃圾收集过程中带来了巨大的损失,远远超过了对对象池的节省。它在生产过程中大约一个月后,我把整个水池都挖了出来,让所有的东西都充满活力,然后我就用它完成了。

#14


2  

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.

编辑:好吧,真的不行。我实现了它,并认为如果它失败了,有时这对我来说是可以的,但它甚至没有一次调用finalize方法。

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()的一个很好的例子,finalize()是在磁盘被破坏之前将其内容写到磁盘上的缓存。因为没有必要每次销毁时都执行它,它只会加速我的程序,我希望我没有做错。

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

#15


2  

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:

删除添加到全局/静态位置(出于需要)的东西是很方便的,并且在删除对象时需要删除这些东西。例如:

    private void addGlobalClickListener() {
        weakAwtEventListener = new WeakAWTEventListener(this);

        Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        if(weakAwtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
        }
    }

#16


1  

Be careful 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()方法。

#17


0  

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.

iirc——您可以使用finalize方法作为为昂贵的资源实现池机制的一种手段——这样它们就不会得到GC。

#18


0  

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.

然而,这个答案表明,至少在使用JIT编译器的java8中,您遇到了意想不到的问题,有时甚至在您从对象维护的流中读取之前就调用终结器。

So even in that situation calling finalize would not be recommended.

因此,即使在这种情况下,调用finalize也不可取。

#19


0  

Personally, I almost never use finalize() except in one rare circumstance: I made a custom generic-type collection, and I wrote a custom finalize() method that does the following:

就我个人而言,我几乎从不使用finalize(),除非是在一种罕见的情况下:我创建了一个定制的泛型类型集合,并编写了一个定制的finalize()方法,它的作用如下:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

(CompleteObject is an interface I made that lets you specify that you've implemented rarely-implemented Object methods like #finalize(), #hashCode(), and #clone())

(CompleteObject是我创建的一个接口,它允许您指定您实现了很少实现的对象方法,如#finalize()、#hashCode()和#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.

因此,使用姐妹# setdecontivelyfinalize (boolean)方法,使用我的集合的程序可以(帮助)保证销毁对该集合的引用也会破坏对其内容的引用,并处理任何可能让JVM在无意中存活的窗口。我还考虑过停止任何线程,但这打开了一个全新的蠕虫。

#20


0  

The resources (File, Socket, Stream etc.) need to be closed once we are done with their usage. They generally have close() method which we generally call in finally section of try-catch statements. Sometimes finalize() 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.

资源(文件、套接字、流等)在使用完毕后需要关闭。它们通常有close()方法,我们通常在try-catch语句的最后部分调用。有时finalize()也可以被少数开发人员使用,但在IMO上,这不是一种合适的方式,因为无法保证finalize总是被调用。

In Java 7 we have got try-with-resources statement which can be used like:

在Java 7中,我们有try-with-resources语句,可以使用如下内容:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

In the above example try-with-resource will automatically close the resource BufferedReader by invoking close() 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.

在上面的示例中,try-with-resource将通过调用close()方法自动关闭资源缓冲阅读器。如果需要,我们还可以在自己的类中实现close,并以类似的方式使用它。在我看来,理解起来更简洁。

#21


0  

As a side note:

边注:

An object that overrides finalize() is treated specially by the garbage collector. Usually, an object is immediately destroyed during the collection cycle after the object is no longer in scope. However, finalizable objects are instead moved to a queue, where separate finalization threads will drain the queue and run the finalize() method on each object. Once the finalize() method terminates, the object will at last be ready for garbage collection in the next cycle.

重写finalize()的对象由垃圾收集器专门处理。通常,在对象不在范围内之后,对象在收集周期中立即被销毁。然而,可终结的对象被转移到一个队列中,在这个队列中,单独的finalization线程将耗尽队列并在每个对象上运行finalize()方法。一旦finalize()方法终止,对象最终将在下一个循环中为垃圾收集做好准备。

finalize deprecated on java-9

完成在java-9弃用