如何确保 finalize() 总是被调用(思考 Java 练习)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12030641/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:20:54  来源:igfitidea点击:

How to ensure finalize() is always called (Thinking in Java exercise)

javagarbage-collectionfinalize

提问by MattDs17

I'm slowly working through Bruce Eckel's Thinking in Java 4th edition, and the following problem has me stumped:

我正在慢慢研究 Bruce Eckel在 Java 4th edition 中思考,以下问题让我难倒:

Create a class with a finalize( ) method that prints a message. In main( ), create an object of your class. Modify the previous exercise so that your finalize( ) will always be called.

创建一个带有打印消息的 finalize( ) 方法的类。在 main() 中,创建一个你的类的对象。修改前面的练习,以便您的 finalize() 将始终被调用。

This is what I have coded:

这是我编码的:

public class Horse {
    boolean inStable;
    Horse(boolean in){
        inStable = in;
    }   
    public void finalize(){
        if (!inStable) System.out.print("Error: A horse is out of its stable!");
    }
}
public class MainWindow {
    public static void main(String[] args) {
        Horse h = new Horse(false);
        h = new Horse(true);
        System.gc();
    }
}

It creates a new Horseobject with the boolean inStableset to false. Now, in the finalize()method, it checks to see if inStableis false. If it is, it prints a message.

它创建一个Horse布尔值inStable设置为的新对象false。现在,在该finalize()方法中,它会检查是否inStablefalse。如果是,它会打印一条消息。

Unfortunately, no message is printed. Since the condition evaluates to true, my guess is that finalize()is not being called in the first place. I have run the program numerous times, and have seen the error message print only a couple of times. I was under the impression that when System.gc()is called, the garbage collector will collect any objects that aren't referenced.

不幸的是,没有打印任何消息。由于条件评估为true,我的猜测是finalize()首先没有被调用。我已经多次运行该程序,并且只看到错误消息打印了几次。我的印象是,当System.gc()被调用时,垃圾收集器将收集任何未引用的对象。

Googling a correct answer gave me this link, which gives much more detailed, complicated code. It uses methods I haven't seen before, such as System.runFinalization(), Runtime.getRuntime(), and System.runFinalizersOnExit().

谷歌搜索一个正确的答案给了我这个链接,它提供了更详细、更复杂的代码。它使用我以前没有见过的方法,如System.runFinalization()Runtime.getRuntime()System.runFinalizersOnExit()

Is anybody able to give me a better understanding of how finalize()works and how to force it to run, or walk me through what is being done in the solution code?

有没有人能让我更好地了解如何finalize()工作以及如何强制它运行,或者引导我了解解决方案代码中正在执行的操作?

回答by Mark Byers

When the garbage collector finds an object that is eligible for collection but has a finalizerit does not deallocate it immediately. The garbage collector tries to complete as quickly as possible, so it just adds the object to a list of objects with pending finalizers. The finalizer is called later on a separate thread.

当垃圾收集器发现一个符合收集条件但有 的对象时,finalizer它不会立即释放它。垃圾收集器尝试尽快完成,因此它只是将对象添加到具有挂起终结器的对象列表中。终结器稍后在单独的线程上调用。

You can tell the system to try to run pending finalizers immediately by calling the method System.runFinalizationafter a garbage collection.

您可以通过System.runFinalization在垃圾回收后调用该方法来告诉系统尝试立即运行挂起的终结器。

But if you want to forcethe finalizer to run, you have to call it yourself. The garbage collector does not guaranteethat any objects will be collected or that the finalizers will be called. It only makes a "best effort". However it is rare that you would ever need to force a finalizer to run in real code.

但是如果你想强制终结器运行,你必须自己调用它。垃圾收集器不保证将收集任何对象或调用终结器。它只会“尽最大努力”。但是,您很少需要强制终结器在实际代码中运行。

回答by supercat

Outside of toy scenarios, it's generally not possible to ensure that a finalizewill always be called on objects to which no "meaningful" references exist, because the garbage collector has no way of knowing which references are "meaningful". For example, an ArrayList-like object might have a "clear" method which sets its count to zero, and makes all elements within the backing array eligible to be overwritten by future Addcalls, but doesn't actually clear the elements in that backing array. If the object has an array of size 50, and its Countis 23, then there may be no execution path by which code could ever examine the references stored in the last 27 slots of the array, but there would be no way for the garbage-collector to know that. Consequently, the garbage-collector would never call finalizeon objects in those slots unless or until the container overwrote those array slots, the container abandoned the array (perhaps in favor of a smaller one), or all rooted references to the container itself were destroyed or otherwise ceased to exist.

在玩具场景之外,通常无法确保finalize始终在不存在“有意义”引用的对象上调用a ,因为垃圾收集器无法知道哪些引用是“有意义的”。例如,一个ArrayList-like 对象可能有一个“清除”方法,该方法将其计数设置为零,并使支持数组中的所有元素有资格被未来Add调用覆盖,但实际上并不清除该支持数组中的元素。如果对象有一个大小为 50 的数组,并且它的大小Count为 23,那么可能没有执行路径可以让代码检查存储在数组最后 27 个槽中的引用,但是垃圾就没有办法了——收藏家知道。最后,finalize在这些插槽中的对象上,除非或直到容器覆盖这些数组插槽,容器放弃了该数组(可能赞成使用较小的数组),或者对容器本身的所有根引用都被破坏或以其他方式不复存在。

There are various means to encourage the system to call finalizeon any objects for which no strong rooted references happen to exist (which seems to be the point of the question, and which other answers have already covered), but I think it's important to note the distinction between the set of objects to which strong rooted references exist, and the set of objects that code may be interested in. The two sets largely overlap, but each set can contain objects not in the other. Objects' finalizers` run when the GC determines that the objects would no longer exist but for the existence of finalizers; that may or may not coincide with the time code they cease being of interest to anyone. While it would be helpful if one could cause finalizers to run on all objects that have ceased to be of interest, that is in general not possible.

有多种方式鼓励系统调用 finalize在任何不存在强根引用的对象上(这似乎是问题的重点,并且其他答案已经涵盖),但我认为重要的是要注意强根引用的对象集之间的区别引用存在,以及代码可能感兴趣的对象集。这两个集合在很大程度上重叠,但每个集合都可以包含另一个集合中没有的对象。对象的终结器在 GC 确定对象将不再存在时运行,除非终结器存在;这可能与他们不再对任何人感兴趣的时间码一致,也可能不一致。虽然如果可以让终结器在所有不再感兴趣的对象上运行会很有帮助,但这通常是不可能的。

回答by lophyxp

run new constructor() and System.gc() more than twice.

运行 new constructor() 和 System.gc() 两次以上。

public class Horse {
    boolean inStable;
    Horse(boolean in){
        inStable = in;
    }   
    public void finalize(){
        if (!inStable) System.out.print("Error: A horse is out of its stable!");
    }
}
public class MainWindow {
    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            Horse h = new Horse(false);
            h = new Horse(true);
            System.gc();
        }
    }
}  

回答by Seeder

A call to garabage collecter (System.gc()) method suggeststhat the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse (i.e its just a suggestion to the jvm, and does not bind it to perform the action then and there, it may or may not do the same). When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects. finalize() is called by the garbage collector on an object when garbage collection determines that there are no more references to the object

对 garabage collecter( System.gc()) 方法的调用表明Java 虚拟机花费精力来回收未使用的对象,以使它们当前占用的内存可用于快速重用(即它只是对 jvm 的一个建议,并没有绑定它来执行然后和那里的动作,它可能会也可能不会这样做)。当控制从方法调用返回时,Java 虚拟机已尽最大努力从所有丢弃的对象中回收空间。当垃圾收集器确定不再有对该对象的引用时,垃圾收集器会在该对象上调用 finalize()

回答by ankush981

Here's what worked for me (partially, but it does illustrate the idea):

这是对我有用的(部分,但它确实说明了这个想法):

class OLoad {

    public void finalize() {
        System.out.println("I'm melting!");
    }
}

public class TempClass {

    public static void main(String[] args) {
        new OLoad();
        System.gc();
    }
}

The line new OLoad();does the trick, as it creates an object with no reference attached. This helps System.gc()run the finalize() method as it detects an object with no reference. Saying something like OLoad o1 = new OLoad();will not work as it will create a reference that lives until the end of main(). Unfortunately, this works most of the time. As others pointed out, there's no way to ensure finalize()will be always called, except to call it yourself.

new OLoad(); 这样做的技巧,因为它创建了一个没有附加引用的对象。这有助于System.gc()运行 finalize() 方法,因为它检测到没有引用的对象。说像OLoad o1 = new OLoad(); 不会工作,因为它会创建一个引用,该引用一直存在到 main() 结束。不幸的是,这在大多数情况下都有效。正如其他人指出的那样,除了自己调用之外,无法确保始终调用finalize()