如何确保始终调用finalize()(在Java练习中思考)

时间:2012-08-19 22:58:42

标签: java garbage-collection finalize

我正在慢慢完成Bruce Eckel的 Thinking in Java 4th edition ,以下问题让我感到难过:

  

使用finalize()方法创建一个打印消息的类。在main()中,创建一个类的对象。修改上一个练习,以便始终调用finalize()。

这就是我编码的内容:

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();
    }
}

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

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

Google搜索正确的答案给了我this link,它提供了更加详细,复杂的代码。它使用我以前从未见过的方法,例如System.runFinalization()Runtime.getRuntime()System.runFinalizersOnExit()

是否有人能够让我更好地了解finalize()如何工作以及如何强制它运行,或者让我了解解决方案代码中的内容?

5 个答案:

答案 0 :(得分:14)

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

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

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

答案 1 :(得分:1)

在玩具场景之外,通常不可能确保始终在没有“有意义”引用的对象上调用finalize,因为垃圾收集器无法知道哪些引用是“有意义的” ”。例如,类似ArrayList的对象可能具有“清除”方法,该方法将其计数设置为零,并使后备阵列中的所有元素都有资格被未来的Add调用覆盖,但不会实际上清除了该后备阵列中的元素。如果对象有一个大小为50的数组,并且它的Count是23,那么可能没有执行路径可以通过它来检查存储在数组的最后27个插槽中的引用,但是没有垃圾收集者知道这一点的方式。因此,垃圾收集器永远不会在这些插槽中的对象上调用finalize,除非或直到容器覆盖了那些数组插槽,容器放弃了数组(可能有利于更小的数组),或者所有有根引用的数组。容器本身被破坏或以其他方式不再存在。

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

class OLoad {

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

public class TempClass {

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

new OLoad(); 可以解决问题,因为它会创建一个没有附加引用的对象。这有助于 System.gc()运行finalize()方法,因为它检测到没有引用的对象。说像 OLoad o1 = new OLoad(); 之类的东西不会起作用,因为它会创建一个直到main()结束的引用。不幸的是,这大部分时间都适用。正如其他人所指出的那样,除了自己调用之外,没有办法确保总是调用 finalize()

答案 4 :(得分:0)

运行新构造函数()和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();
        }
    }
}