2017-06-14 40 views
7

为什么这个下面的代码Java的内存不足的错误

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循环内创建列表,要么在其外部创建列表,但我不确定原因为什么发生这种情况。

+4

简单的内存泄漏...对于第二个示例,您简化了垃圾收集器的工作 – FieryCat

+0

Java使用堆垃圾收集器存储新对象的堆。当堆已满并且gc无法容纳新对象时抛出OutOfMemoryError。当没有足够的本地内存来加载java类时,也会抛出这个错误。 –

+1

@FieryCat不会简化......在第一种情况下,根本没有任何垃圾收集。 – wvdz

回答

10

在第一种情况下,您只有一个ArrayList实例,并且您不断向其添加新的Object实例,直到内存用完。

在第二种情况下,你在while循环的每个迭代创建一个新的ArrayList并添加1000000Object实例它,这意味着在上一次迭代中创建的ArrayList,它包含可以被垃圾收集1000000Object的情况下,因为程序不再提及它们。

请注意,如果新创建的Object的创建速度比垃圾回收器可以释放旧垃圾回收器的速度更快,但第二个代码段也会导致内存不足错误,但这取决于JVM的实现。

5

在第一个片段中,列表在循环外部创建(并保留!),所以您只要不断添加元素直到消耗所有可用内存为止。

在第二个片段中,while循环的每次迭代都会创建一个新的对象ArrayList。由于迭代结束后不再持有对该实例的引用,因此此列表有资格进行垃圾回收,因此旧列表不断耗尽,并且不会耗尽内存。

4

在第二种情况下,创建的列表(以及添加元素的位置)已超出范围并符合GC要求。这将被GCed记忆为新的ArrayList。因此,对于每次迭代,创建一个新的ArrayList,然后它变为符合GC(循环结束时)。因此,当内存不足时,这些对象将被GCed。

在第一种情况下,您将元素添加到相同的ArrayList。没有任何东西正在被注入。

3

因为当您在while循环内创建列表时,您的上一个列表被转储并且您有一个新的空列表。之后,您的内存将被java垃圾收集器释放,并将1000000个元素添加到列表中。然后一个新的列表被创建并且一切都重复。

2

在第一个示例中,您将创建一个列表,向其中添加项目,然后循环结束。在第二个例子中,你创建一个列表,添加一些东西,然后创建一个新的列表,给它添加一堆东西,然后无限重复。由于在第一个例子中你的变量是在循环外创建的,因此只有一个列表需要填充。

3

在第一种情况下,列表对象是在while循环之外声明的,该循环再次无限期地运行(如while(true)),因此它继续添加直到内存用完,而第二个因为你已经在while语句中声明了列表,最大值被限制在for循环的迭代次数。

每次存在for循环时,列表​​对象都将被重置,即创建的新对象将开始添加,因此您有一个上限。旧对象被垃圾收集,从而清除JVM。

3

这个问题很好的回答了@Eran,@TheLostMind和所有,所以我没有提出同样的观点,我只是想借此机会就如何帮助“延缓”内存不足例外。

运行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()); 
       } 
      } 
     } 
    } 
} 
2

两个代码之间的唯一区别是List list = new ArrayList <>()的位置;线。对于第一个代码,ArrayList在while循环之外声明,并且它将无限数量的对象添加到一个ArrayList实例中,以便发生内存不足。另一方面,第二个在while循环中声明ArrayList,以便在每个循环周期(许多ArrayList实例)之后实例化一个新的ArrayList。根据Java中的Garbage Collector规则,前一个周期的实例将被删除,因为它不再被指出。因此,Java中的GC防止了第二种情况下的内存不足。