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