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");
}
}
Однако простое добавление единственной строчки в уловке покажет вам, о чем я говорю:
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 обнаруживает, что список больше не будет использоваться (это обнаружение также может быть оптимизацией, сделанной во время компиляции). Итак, когда мы достигаем оператора печати, память кучи была освобождена почти полностью, так что теперь у нас есть большой запас маневра для продолжения. Это лучший случай.
Однако, если код организован так, как если бы список ll
использовался после того, как OOME был пойман, JVM не сможет его собрать. Это происходит во втором фрагменте. OOME, запускаемый новым созданием Long, улавливается, но вскоре мы создаем новый объект (String в System.out,println
строке), а куча почти заполнена, поэтому создается новый OOME. Это наихудший сценарий: мы пытались создать новый объект, у нас ничего не вышло, мы перехватили OOME, да, но теперь первая инструкция, требующая новой памяти кучи (например, создание нового объекта), вызовет новый OOME. Подумайте об этом, что еще мы можем сделать в этот момент, когда осталось так мало памяти? Наверное, просто ухожу. Отсюда и бесполезное.
Одна из причин, по которой JVM не собирает ресурсы, действительно пугает: общий ресурс, который также используется другими потоками. Любой, у кого есть мозг, может увидеть, насколько опасным может быть обнаружение OOME, если его вставить в какое-либо неэкспериментальное приложение любого рода.
Я использую 32-битную JVM для Windows x86 (JRE6). Память по умолчанию для каждого приложения Java составляет 64 МБ.