为什么这个代码
List<Object> list = new ArrayList<>();
while (true) {
for(int i = 0; i < 1000000; i++){
list.add(new Object());
}
}
产生内存不足错误
但是这段代码并没有
while(true) {
List<Object> list = new ArrayList<>();
for(int i = 0; i < 1000000; i++){
list.add(new Object());
}
}
我可以看到它与在while循环内部或在其外部创建的列表有关,但我不确定为什么这种情况发生的原因。
答案 0 :(得分:10)
在第一种情况下,您只有一个ArrayList
实例,并且一直在向它添加新的Object
实例,直到内存不足为止。
在第二种情况下,您在ArrayList
循环的每次迭代中创建一个新while
并向其添加1000000
Object
个实例,这意味着{{1在上一次迭代中创建并且它包含的ArrayList
1000000
个实例可以被垃圾收集,因为程序不再引用它们。
请注意,如果新的Object
创建速度快于垃圾收集器可以释放旧的Object
,则第二个代码段也会导致内存不足错误,但这取决于JVM的实现。
答案 1 :(得分:5)
在第一个片段中,列表在循环外部创建(并保留!),因此您只需不断向其中添加元素,直到消耗掉所有可用内存为止。
在第二个片段中,while
循环的每次迭代都会创建一个新的ArrayList
对象。由于您在迭代结束后不再持有对该实例的引用,因此此列表符合垃圾回收的条件,因此旧列表不断被释放,您不会耗尽您的记忆。
答案 2 :(得分:4)
在第二种情况下,创建的列表(以及添加元素的位置)超出范围且符合GC条件。这将用于为新ArrayList
创建内存。因此,对于每次迭代,都会创建一个新的ArrayList
,然后它就有资格获得GC(当循环结束时)。因此,当内存不足时,这些对象将被GCed。
在第一种情况下,您要将元素添加到同一ArrayList
。什么都没有GC。
答案 3 :(得分:3)
因为当您在while循环中创建列表时,您的上一个列表将被转储,并且您有一个新的空列表。然后你的内存被java垃圾收集器释放,你将1000000个元素添加到列表中。然后创建一个新列表,一切都重复。
答案 4 :(得分:3)
在第一个场景中,列表对象在while循环之外声明,它再次无限期地运行(因为while(true)),因此它一直在添加,直到它耗尽内存,而在第二个因为你已经在while内声明了列表,最大大小被限制为for循环的迭代次数。
每次for循环存在时,重置列表对象,即创建新的列表对象,您开始添加,因此您有一个上限。旧对象被垃圾收集,从而清除JVM。
答案 5 :(得分:3)
@Eran,@ TheLostMind和所有人都很好地回答了这个问题,所以我没有提出同样的观点,我只想借此机会说明SoftReference和WeakReference如何帮助“延迟”内存不足异常。
使用JVM参数作为-Xms64m -Xmx64m
运行以下代码,以便您可以快速查看结果。
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class OOM {
public static void main(String[] args) {
System.out.println(new Date());
try {
scenario1(false, false); // in my box, OOM occurred with average of 2 seconds.
//scenario1(true, false); // in my box, OOM occurred average of 6 seconds.
//scenario1(false, true); // in my box, OOM occurred average of 8 seconds.
} catch (Exception e) {
} catch (Error err){
}
System.out.println(new Date());
}
private static void scenario1(boolean useSoftReference, boolean useWeakReference) {
List<Object> list = new ArrayList<>();
while (true) {
for(int i = 0; i < 1000000; i++){
if(useSoftReference){
list.add(new SoftReference<Object>(new Object()));
} else if(useWeakReference){
list.add(new WeakReference<Object>(new Object()));
} else{
list.add(new Object());
}
}
}
}
}
答案 6 :(得分:2)
在第一个示例中,您创建一个列表,向其中添加项目,然后循环结束。在第二个示例中,您创建一个列表,向其添加内容,然后创建新列表,向其中添加一些内容并重复无限。由于在第一个示例中,您的变量是在循环外创建的,因此只有一个列表需要填充。
答案 7 :(得分:2)
两个代码之间的唯一区别是List list = new ArrayList&lt;&gt;();线。对于第一个代码,在while循环之外声明的ArrayList并且它不断地将无限数量的对象添加到一个ArrayList实例中,因此发生内存不足。另一方面,第二个在while循环内部声明ArrayList,因此它将在每个循环周期(许多ArrayList实例)之后实例化一个新的ArrayList。根据Java中的垃圾收集器规则,之前的循环实例将被删除,因为它不再被指向。因此,Java中的GC可防止第二种情况下的内存不足。