虽然可以捕获OOME,但通常情况下这是无用的,这取决于JVM是否能够在到达catch块时进行垃圾回收,并且此时还剩多少堆内存。
例如,在我的JVM中,该程序可以正常运行至完成:
import java.util.LinkedList;
import java.util.List;
public class OOMErrorTest {
public static void main(String[] args) {
List<Long> ll = new LinkedList<Long>();
try {
long l = 0;
while(true){
ll.add(new Long(l++));
}
} catch(OutOfMemoryError oome){
System.out.println("Error catched!!");
}
System.out.println("Test finished");
}
}
然而,只需在catch块中添加一行,即可理解我的意思:
import java.util.LinkedList;
import java.util.List;
public class OOMErrorTest {
public static void main(String[] args) {
List<Long> ll = new LinkedList<Long>();
try {
long l = 0;
while(true){
ll.add(new Long(l++));
}
} catch(OutOfMemoryError oome){
System.out.println("Error caught!!");
System.out.println("size:" +ll.size());
}
System.out.println("Test finished");
}
}
第一个程序运行良好,因为当到达catch块时,JVM检测到列表不再被使用(此检测也可以是在编译时进行的优化)。因此,当我们到达打印语句时,堆内存已经几乎完全释放,所以我们现在有了广泛的操作余地。这是最好的情况。
然而,如果代码安排得像第二个片段一样,列表“ll”在OOME被捕获后被使用,JVM将无法收集它。 OOME由新的Long创建引发,但很快我们就要创建一个新的对象(在
System.out.println
行中是一个字符串),堆几乎满了,因此会抛出新的OOME。这是最坏的情况:我们尝试创建一个新对象,失败了,我们捕获了OOME,但现在需要新的堆内存(例如:创建新对象)的第一条指令将抛出新的OOME。想想看,在这种情况下,我们还能用那么少的内存做什么?可能只剩下退出,因此我说这是无用的。
JVM未垃圾回收资源的原因之一真的很可怕:与其他线程共享资源并且也在使用该资源。任何人都可以看到,如果将其添加到任何非实验性应用程序中,捕获OOME会有多么危险。
我正在使用Windows x86 32位JVM(JRE6)。每个Java应用程序的默认内存为64MB。