OOMEはキャッチできますが、キャッチに達したときにJVMがいくつかのオブジェクトをガベージコレクションできるかどうか、およびその時点で残っているヒープメモリの数に応じて、通常は役に立たなくなります。
例:私の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に1行追加するだけで、私が話していることがわかります。
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("size:" +ll.size());
}
System.out.println("Test finished");
}
}
キャッチに到達すると、JVMはリストが使用されなくなることを検出するため、正常に実行されます(この検出は、コンパイル時に行われる最適化にもなります)。したがって、printステートメントに到達すると、ヒープメモリはほぼ完全に解放されているので、操作を続行するための広いマージンがあります。これが最良のケースです。
ただし、ll
OOMEがキャッチされた後にリストが使用されるようにコードが配置されている場合、JVMはコードを収集できません。これは2番目のスニペットで発生します。新しいLong作成によってトリガーされたOOMEがキャッチされますが、すぐに新しいオブジェクト(System.out,println
行の文字列)が作成され、ヒープがほぼいっぱいになり、新しいOOMEがスローされます。これは最悪のシナリオです。新しいオブジェクトを作成しようとして失敗し、OOMEをキャッチしましたが、新しいヒープメモリを必要とする最初の命令(たとえば、新しいオブジェクトの作成)が新しいOOMEをスローします。考えてみてください。メモリがほとんど残っていない状態で、この時点で他に何ができるでしょうか。たぶんただ存在します。したがって、役に立たない。
JVMがリソースを収集しない理由の1つは、恐ろしいことです。他のスレッドとの共有リソースもそれを利用しています。実験的なものではないアプリに挿入すると、脳のある人なら誰でもOOMEを捕まえることがいかに危険かがわかります。
Windows x86 32ビットJVM(JRE6)を使用しています。各Javaアプリのデフォルトのメモリは64MBです。