Java 内存不足错误
Java out of memory errors
为什么下面的代码
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(当循环结束时)。因此,当内存不足时,这些对象将被 GCed。
在第一种情况下,您要向同一个 ArrayList
添加元素。没有任何内容被 GC。
因为当您在 while 循环中创建列表时,您之前的列表被转储并且您有一个新的空列表。之后你的内存被 java 垃圾收集器释放,你将 1000000 个元素添加到列表中。然后创建一个新列表,然后一切重复。
在第一个示例中,您创建了一个列表,向其中添加项目,然后循环结束。在第二个示例中,您创建一个列表,向其中添加内容,然后创建一个 new 列表,向其中添加一堆内容并重复 无限次 .因为在第一个例子中你的变量是在循环外创建的,所以只有一个列表要填充。
在第一种情况下,列表对象是在 while 循环之外声明的,它又是 运行 无限期(如 while (true)),因此它会不断添加直到内存不足,而在第二个中,因为您已经在 while 内声明了列表,所以最大大小限制为 for 循环的迭代次数。
每次 for 循环存在时,列表对象都会被重置,即创建新的列表对象并开始添加,因此您有一个上限。旧对象被垃圾收集,从而清除 JVM。
@Eran、@TheLostMind 和所有人都很好地回答了这个问题,所以我没有提出相同的观点,我只是想借此机会说明 SoftReference and WeakReference 如何帮助 "delay" 内存不足异常。
运行 下面的代码将 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());
}
}
}
}
}
两段代码唯一的区别是List list = new ArrayList<>();的位置不同线。对于第一个代码,ArrayList 在 while 循环之外声明,它不断将无限数量的对象添加到一个 ArrayList 实例中,因此发生内存不足。另一方面,第二个在 while 循环中声明 ArrayList,因此它将在每个循环周期(许多 ArrayList 实例)之后实例化一个新的 ArrayList。根据Java中垃圾收集器的规则,前一个循环的实例将被删除,因为它不再被指向。结果,Java中的GC防止了第二种情况下的内存不足。
为什么下面的代码
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(当循环结束时)。因此,当内存不足时,这些对象将被 GCed。
在第一种情况下,您要向同一个 ArrayList
添加元素。没有任何内容被 GC。
因为当您在 while 循环中创建列表时,您之前的列表被转储并且您有一个新的空列表。之后你的内存被 java 垃圾收集器释放,你将 1000000 个元素添加到列表中。然后创建一个新列表,然后一切重复。
在第一个示例中,您创建了一个列表,向其中添加项目,然后循环结束。在第二个示例中,您创建一个列表,向其中添加内容,然后创建一个 new 列表,向其中添加一堆内容并重复 无限次 .因为在第一个例子中你的变量是在循环外创建的,所以只有一个列表要填充。
在第一种情况下,列表对象是在 while 循环之外声明的,它又是 运行 无限期(如 while (true)),因此它会不断添加直到内存不足,而在第二个中,因为您已经在 while 内声明了列表,所以最大大小限制为 for 循环的迭代次数。
每次 for 循环存在时,列表对象都会被重置,即创建新的列表对象并开始添加,因此您有一个上限。旧对象被垃圾收集,从而清除 JVM。
@Eran、@TheLostMind 和所有人都很好地回答了这个问题,所以我没有提出相同的观点,我只是想借此机会说明 SoftReference and WeakReference 如何帮助 "delay" 内存不足异常。
运行 下面的代码将 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());
}
}
}
}
}
两段代码唯一的区别是List list = new ArrayList<>();的位置不同线。对于第一个代码,ArrayList 在 while 循环之外声明,它不断将无限数量的对象添加到一个 ArrayList 实例中,因此发生内存不足。另一方面,第二个在 while 循环中声明 ArrayList,因此它将在每个循环周期(许多 ArrayList 实例)之后实例化一个新的 ArrayList。根据Java中垃圾收集器的规则,前一个循环的实例将被删除,因为它不再被指向。结果,Java中的GC防止了第二种情况下的内存不足。