GC不清除范围内的对象

时间:2016-06-16 13:47:07

标签: java java-8 jit

我早就读过一篇文章而且我似乎找不到它。它解释了以下行为,其中范围内的对象可能仍未被清除。在任何情况下,您可以帮助解释为什么以下结束OutOfMemoryError

public static void main(String[] args) {
    List<A> collect = null;
    int i=0;
    while(true){
        System.out.println(i++);
        collect = IntStream.range(0, 10_00_000).mapToObj(x -> new A(x + "")).collect(Collectors.toList());
    }
}

class A{
    String temp;
    A(String a){
        this.temp = a;
    }
}

当我在finalize中使用print语句放置A时,它永远不会打印出来。并且在几次迭代之后,实例以OOME结束。为什么gc清除之前创建的collect。并且迭代次数也不是一成不变的,范围从3到偶数100,但最终失败为-Xmx500m

其次,为了好玩,我在JIT禁用-Djava.compiler=NONE的情况下运行了上述程序。它按预期永久运行。 JIT如何影响它?

PS:当我在while子句中包含System.gc();时,它会按预期继续运行。

1 个答案:

答案 0 :(得分:8)

我无法使用-Xmx500m重现您的结果,但使用-Xmx200m。一致地,当您添加finalize()方法时,程序将失败,因为终结器是问题的原因。但是, 在我的系统中打印了一些"finalized"消息。如果没有finalize()方法,它会永远运行(实际上,直到我杀死它)。

回收普通对象的空间没有问题,但是当你添加一个非常重要的finalize()方法时,你正在积极地阻止对象超出范围,因为它们现在必须排队以进行最终化。

虽然JVM将始终执行垃圾收集,尝试释放所有未使用的对象,但在抛出OutOfMemoryError之前,对于挂起在终结队列中的对象没有这样的保证。通过禁用JIT来减慢主要任务可能确实允许最终化处理更多元素。请记住,将主要任务和finalize方法打印到System.out都具有同步效果,这可能会使处理的时间变得至关重要。

可以影响结果的是几个环境方面。由于没有关于哪个线程将执行终结器的保证,因此最终化可以使用所有未使用的CPU核心(并且您的主线程仅使用一个)。 实际的垃圾收集算法(JVM允许选择几种不同算法中的一种)也会产生影响。