15

我正在慢慢研究 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()方法中,它检查是否inStablefalse。如果是,它会打印一条消息。

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

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

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

4

5 回答 5

17

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

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

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

于 2012-08-19T23:03:56.243 回答
1

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

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

于 2013-06-13T16:16:27.233 回答
0

对垃圾收集System.gc()(那时和那里的行动,它可能会或可能不会做同样的事情)。当控制从方法调用返回时,Java 虚拟机已尽最大努力从所有丢弃的对象中回收空间。当垃圾收集器确定不再有对该对象的引用时,垃圾收集器对对象调用 finalize()

于 2012-08-19T23:33:00.957 回答
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() 。

于 2013-06-11T03:19:23.413 回答
0

运行 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();
        }
    }
}  
于 2014-03-25T10:06:47.473 回答