2017-06-14 66 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防止了第二種情況下的內存不足。