我早就读过一篇文章而且我似乎找不到它。它解释了以下行为,其中范围内的对象可能仍未被清除。在任何情况下,您可以帮助解释为什么以下结束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();
时,它会按预期继续运行。
答案 0 :(得分:8)
我无法使用-Xmx500m
重现您的结果,但使用-Xmx200m
。一致地,当您添加finalize()
方法时,程序将失败,因为终结器是问题的原因。但是, 在我的系统中打印了一些"finalized"
消息。如果没有finalize()
方法,它会永远运行(实际上,直到我杀死它)。
回收普通对象的空间没有问题,但是当你添加一个非常重要的finalize()
方法时,你正在积极地阻止对象超出范围,因为它们现在必须排队以进行最终化。
虽然JVM将始终执行垃圾收集,尝试释放所有未使用的对象,但在抛出OutOfMemoryError
之前,对于挂起在终结队列中的对象没有这样的保证。通过禁用JIT来减慢主要任务可能确实允许最终化处理更多元素。请记住,将主要任务和finalize方法打印到System.out
都具有同步效果,这可能会使处理的时间变得至关重要。
可以影响结果的是几个环境方面。由于没有关于哪个线程将执行终结器的保证,因此最终化可以使用所有未使用的CPU核心(并且您的主线程仅使用一个)。 实际的垃圾收集算法(JVM允许选择几种不同算法中的一种)也会产生影响。