一个`finalize`调用如何破坏GC/JVM?

12

在调查为什么在Eclipse中运行Java程序和从命令行运行时ThreadPoolExecutor的行为不同时,我编写了一个测试,它会抛出一个非常奇怪的OutOfMemoryError(最大内存=256 Mb)

class A {
    byte[] buf = new byte[150_000_000];

    protected void finalize() {
        int i = 1;
    }
}

A a1 = new A();
a1 = null;
A a2 = new A();

int i = 1注释掉,测试就可以正常工作。据我所知,当finalize为空时,HotSpot会忽略它。但是,一个几乎为空的finalize调用如何破坏GC / JVM呢?


2
有趣的问题,+1和一颗星 - Sri Harsha Chilakapati
1个回答

9
“但是,仅仅一个空的 finalize 调用如何会破坏 GC/JVM?”
当存在 finalizer 时,对象比没有 finalizer 的情况多存活一轮垃圾回收(因为对象本身必须在完成 finalize 前保持存活状态)。因此,如果你有一个带有 finalizer 的大对象,那么在没有 finalizer 的情况下不会发生的 OutOfMemoryError 就会自然而然地发生。
在这段代码中:
A a1 = new A();
a1 = null;
A a2 = new A();

... GC会在最后一行触发,以尝试找到足够的内存来分配第二个A。不幸的是,它无法垃圾回收第一个A(以及它所引用的数组),因为finalizer尚未运行。它不会等待finalizer完成,然后再尝试进行垃圾回收 - 它只会抛出OutOfMemoryError


我不是很明白:对象无论如何都会被创建吗?还是说它不会被创建?你的意思是a1和a2是惰性创建的吗? - fge
2
@fge:关键是,当没有终结器时,垃圾收集器可以立即回收第一个A实例和数组,当它需要为第二个A实例(和第二个数组)找到更多内存时。当有终结器时,它无法这样做。 - Jon Skeet
好的,现在这就有很多意义了!感谢您的解释。 - fge

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