Поймать java.lang.OutOfMemoryError? - PullRequest
92 голосов
/ 21 апреля 2010

Документация для java.lang.Error говорит:

Ошибка - это подкласс Throwable, который указывает на серьезные проблемы, которые разумное приложение не должно пытаться поймать

Но поскольку java.lang.Error является подклассом java.lang.Throwable, я могу поймать этот тип Throwable.

Я понимаю, почему не стоит ловить такого рода исключения. Насколько я понимаю, если мы решим перехватить его, обработчик перехвата не должен выделять какую-либо память самостоятельно. В противном случае OutOfMemoryError будет снова брошено.

Итак, мой вопрос:

  1. Существуют ли какие-либо сценарии реального мира при ловле java.lang.OutOfMemoryError, может быть хорошей идеей?
  2. Если мы решим перехватить java.lang.OutOfMemoryError, как мы можем убедиться, что обработчик перехвата не выделяет память самостоятельно (какие-либо инструменты или лучшие практики)?

Ответы [ 14 ]

2 голосов
/ 09 сентября 2011

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 МБ.

2 голосов
/ 21 апреля 2010

Единственная причина, по которой я могу подумать, почему перехват ошибок OOM может заключаться в том, что у вас есть массивные структуры данных, которые вы больше не используете, и вы можете установить в ноль и освободить часть памяти. Но (1) это означает, что вы тратите впустую память, и вы должны исправить свой код, а не просто хромать после OOME, и (2) даже если вы его поймали, что бы вы сделали? OOM может произойти в любое время, потенциально оставляя все наполовину выполненным.

0 голосов
/ 21 апреля 2010

Вы можете поймать все что угодно в Throwable, вообще говоря, вы должны ловить только подклассы Exception, исключая RuntimeException (хотя большая часть разработчиков также ловит RuntimeException ... но это никогда не было целью разработчиков языка).

Если бы вы поймали OutOfMemoryError, что бы вы сделали?У виртуальной машины недостаточно памяти, в основном все, что вы можете сделать, это выйти.Вы, вероятно, даже не можете открыть диалоговое окно, чтобы сказать им, что у вас недостаточно памяти, так как это заняло бы память: -)

Виртуальная машина выдает ошибку OutOfMemoryError, когда действительно не хватает памяти (действительно, все ошибки должны указывать на неисправимые ситуации) и на самом деле не должно быть ничего, что вы можете сделать, чтобы справиться с этим.

Что нужно сделать, это выяснить, почему у вас заканчивается память (используйте профилировщик, как в NetBeans) и убедитесь, что вынет утечек памятиЕсли у вас нет утечек памяти, увеличьте объем памяти, выделяемой для виртуальной машины.

0 голосов
/ 21 апреля 2010
  1. Зависит от того, как вы определяете «хорошо». Мы делаем это в нашем глючном веб-приложении, и оно работает большую часть времени (к счастью, теперь OutOfMemory не происходит из-за несвязанного исправления). Тем не менее, даже если вы поймаете его, он все равно может сломать какой-то важный код: если у вас несколько потоков, выделение памяти может произойти сбой в любом из них. Таким образом, в зависимости от вашего приложения вероятность того, что он будет необратимо сломан, составляет 10--90%.
  2. Насколько я понимаю, раскручивание большого стека в пути сделает недействительными столько ссылок и, таким образом, освободит столько памяти, что вам на это наплевать.

РЕДАКТИРОВАТЬ: Я предлагаю вам попробовать. Скажем, напишите программу, которая рекурсивно вызывает функцию, которая постепенно выделяет больше памяти. Поймай OutOfMemoryError и посмотри, сможешь ли ты продолжить с этого момента. Согласно моему опыту, вы сможете, хотя в моем случае это происходило на сервере WebLogic, так что, возможно, была какая-то черная магия.

...