GC 不清除范围内的对象

GC not clearing objects in scope

我很久以前看过一篇文章,但我似乎没有找到它。它解释了以下行为,其中范围内的对象可能仍未被清除。无论如何,您能否帮助解释为什么以下内容以 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 放入 A 时,它永远不会被打印出来。并且实例在几次迭代后以 OOME 结束。为什么 gc 不清除以前创建的 collect。而且迭代次数也不是恒定的,它 运行 从 3 到甚至 100 但最终失败 -Xmx500m

其次,为了好玩,我 运行 上面的程序禁用了 JIT -Djava.compiler=NONE。它会按预期永远运行。 JIT 如何影响它?

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

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

回收普通对象的 space 没有问题,但是当您添加一个重要的 finalize() 方法时,您正在主动防止对象超出范围,因为它们现在必须排队等候完成。

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

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