JVM/GC 在程序/线程退出时会调用 `finalize()` 吗?

13

PS:我确实知道如何正确清除,而不依赖于finalize()

Java不能保证在程序退出时进行垃圾回收吗?例如,假设我将一些数据存储在缓存中,而不是频繁地对其进行序列化。我还使用了finalize(),希望如果我的程序正常退出(除了崩溃以外的任何原因),那么缓存将通过finalize()方法被写入到数据库/文件/某些存储中。但是根据以下小实验,似乎JVM并没有“优雅地”清理内存,它只是退出。 Java规范(请参见程序退出)没有说明有关退出时如何处理内存/ GC的内容。或者我应该查看规范的不同部分吗?下面是一个使用1.6.0.27 64位Windows 7的示例(输出在末尾)。

public class Main {

        // just so GC might feel there is something to free..
    private int[] intarr = new int[10000];

    public static void main(String[] args) {
        System.out.println("entry");
        Main m = new Main();
        m.foo();
        m = new Main();
        // System.gc();
        m.foo();
        m = null;
        // System.gc();
        System.out.println("before System.exit(0);");
        System.exit(0);
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize()");
        super.finalize();
    }

    public void foo() { System.out.println("foo()"); }
}

/*
 * Prints:
 * entry 
 * foo() 
 * foo() 
 * before System.exit(0);
 */

变化:

  • 如果我取消注释任何一个System.gc(),那么就不会调用finalize()
  • 如果我取消注释两个System.gc(),那么finalize()会被调用两次。
  • 无论是否调用System.exit(),都不会影响是否调用finalize()

你面临的问题是程序随时可能突然终止。你可能还需要能够在重启时进行清理。 - Peter Lawrey
2个回答

11
不,Java不能保证程序退出时会触发GC。 如果您想在退出时执行操作,请使用Runtime.addShutdownHook方法。阅读 Sun 文章了解SPEC的详细信息。
Java平台规范对垃圾回收的实际工作原理没有多少保证。 Java虚拟机规范(JVMS)对内存管理有以下说法:堆是在虚拟机启动时创建的。对象的堆存储由自动存储管理系统(称为垃圾收集器)回收;对象永远不会被显式释放。 Java虚拟机不假定任何特定类型的自动存储管理系统,并且根据实现者的系统要求可以选择存储管理技术。垃圾回收模型未得到严格定义的事实实际上非常重要和有用-严格定义的垃圾回收模型可能无法在所有平台上实现。 同样,它可能排除有用的优化并损害平台的长期性能。
虽然没有包含完整定义所需的垃圾收集器行为的地方,但Java语言规范和JVMS中的许多部分隐含地指定了大部分GC模型。虽然无法保证遵守规范的虚拟机将完全按照指定的过程进行,但所有符合规范的虚拟机都共享本章描述的基本对象生命周期。

它(例如规范)在任何地方都有说明吗? - Kashyap
1
尽管您提供的描述仍然没有明确说明退出时不会执行垃圾收集,但我认为已经足够清楚地表明它留给了JVM的实现。感谢关于addShutdownHooks()的信息,我之前并不知道。 - Kashyap
1
规范中没有任何负面声明表明“GC不一定在退出时运行”。规范中故意模棱两可的语言和我引用的广泛陈述就是所有声明。 - Amir Afghani
1
的确,我当然并不是想看到确切的声明,但我至少希望在规范中看到描述JVM退出时职责的内容。无论如何,这更多是为了学习而不是必要,所以您的答案完全可以接受。 - Kashyap
我只是想进一步澄清这个问题...并不保证GC会执行清理操作...即使它执行了,也不能保证GC会移除对象并调用finalize()方法。即使在退出之前调用System.gc(),对象仍然可能没有触发finalize()方法。 - Philip Couling

4

请参考Runtime.runFinalizersOnExit()。需要注意的是,该方法已经被废弃,同时需要注意其他相关注释。从这些注释中可以合理地推断出,默认情况下该功能是关闭的,但也需要注意它可以被打开。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接