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 循环内部或外部创建的列表明显有关,但我不确定发生这种情况的原因

在第

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

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

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

在第一个代码段中,列表是在循环之外创建(并保留!(的,因此您只需无休止地向其添加元素,直到耗尽所有可用内存。

在第二个代码段中,while循环的每次迭代都会创建一个新的ArrayList对象。由于迭代结束后不再持有对该实例的引用,因此此列表符合垃圾回收条件,因此旧列表不断被释放,并且您不会耗尽内存。

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

在第一种情况下,您将元素添加到同一ArrayList。没有任何东西被GC化。

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

在第一种情况下,list 对象在 while 循环之外声明,该循环再次无限期运行(as while (true ((,因此它会继续添加,直到内存不足,而在第二种情况下,由于您已在 while 中声明了列表,最大大小被限制为 for 循环的迭代次数。

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

这个问题

得到了@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());
                }
            }
        }
    }
}

在第一个示例中,您创建一个列表,向其添加项,然后循环结束。在第二个示例中,您创建一个列表,向其添加内容,然后创建一个列表,向其添加一堆内容并无限重复。由于在第一个示例中,您的变量是在循环外部创建的,因此只有 1 个列表需要填充。

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

最新更新