Документация для java.lang.Error
говорит:
Ошибка - это подкласс Throwable, который указывает на серьезные проблемы, которые разумное приложение не должно пытаться уловить.
Но поскольку java.lang.Error
это подкласс java.lang.Throwable
, я могу поймать этот тип Throwable.
Я понимаю, почему ловить такое исключение - не лучшая идея. Насколько я понимаю, если мы решим его поймать, обработчик catch не должен выделять память сам по себе. В противном случае OutOfMemoryError
будет брошен еще раз.
Итак, мой вопрос:
- Существуют ли какие-либо реальные сценарии, когда ловля
java.lang.OutOfMemoryError
может быть хорошей идеей? - Если мы решим поймать
java.lang.OutOfMemoryError
, как мы можем убедиться, что обработчик catch не выделяет память сам по себе (какие-либо инструменты или лучшие практики)?
java
try-catch
out-of-memory
Денис Баженов
источник
источник
Ответы:
Я согласен и не согласен с большинством приведенных здесь ответов.
Существует ряд сценариев, в которых вы, возможно, захотите уловить,
OutOfMemoryError
и, по моему опыту (на JVM Windows и Solaris), только очень редко это становитсяOutOfMemoryError
смертельным ударом для JVM.Есть только одна веская причина для того, чтобы поймать ошибку,
OutOfMemoryError
и она состоит в том, чтобы изящно завершить работу, аккуратно освободив ресурсы и записав причину сбоя как можно лучше (если это все еще возможно).Как правило, это
OutOfMemoryError
происходит из-за выделения памяти блока, которое не может быть удовлетворено оставшимися ресурсами кучи.Когда
Error
выбрасывается, куча содержит то же количество выделенных объектов, что и до неудачного выделения, и теперь пора отбросить ссылки на объекты времени выполнения, чтобы освободить еще больше памяти, которая может потребоваться для очистки. В этих случаях может быть даже возможно продолжить, но это определенно будет плохой идеей, поскольку вы никогда не можете быть на 100% уверены, что JVM находится в исправном состоянии.Демонстрация,
OutOfMemoryError
которая не означает, что JVM не хватает памяти в блоке catch:Вывод этого кода:
Если запускается что-то критическое, я обычно ловлю его
Error
, записываю в syserr, затем записываю его, используя выбранную мной структуру ведения журнала, затем перехожу к освобождению ресурсов и закрытию в чистом виде. Что может случиться худшего? JVM все равно умирает (или уже мертва), и, поймав ее,Error
есть, по крайней мере, шанс на очистку.Предостережение заключается в том, что вы должны нацеливаться на перехват этих типов ошибок только в тех местах, где возможна очистка. Не надо
catch(Throwable t) {}
везде накидывать одеяло или такую ерунду.источник
OutOfMemoryError
могло быть выброшено из точки, которая поместила вашу программу в несогласованное состояние, потому что она может быть выброшена в любое время. См stackoverflow.com/questions/8728866/...Вы можете восстановить из нее:
Но есть ли в этом смысл? Что еще ты хотел бы сделать? Зачем изначально выделять столько памяти? Меньше памяти тоже нормально? Почему ты все равно не используешь его? Или, если это невозможно, почему бы просто не дать JVM с самого начала больше памяти?
Вернуться к вашим вопросам:
Ничего не приходит в голову.
Зависит от того, что вызвало ошибку OOME. Если это заявлено вне
try
блока и происходило поэтапно, то ваши шансы невелики. Вы можете заранее зарезервировать место в памяти:а затем установите его в ноль во время OOME:
Конечно, в этом нет никакого смысла;) Просто дайте JVM достаточно памяти, как того требует ваше приложение. При необходимости запустите профилировщик.
источник
null
?В общем, пытаться поймать OOM и восстановиться после него - плохая идея.
OOME также мог быть брошен в другие потоки, включая потоки, о которых ваше приложение даже не знает. Любые такие потоки теперь будут мертвыми, и все, что ожидало уведомления, могло застрять навсегда. Короче говоря, ваше приложение может быть окончательно сломано.
Даже если вы успешно восстановитесь, ваша JVM может все еще страдать от нехватки памяти, и в результате ваше приложение будет работать ужасно.
Лучшее, что можно сделать с OOME, - это позволить JVM умереть.
(Предполагается, что JVM действительно умирает. Например, OOM в потоке сервлета Tomcat не уничтожают JVM, и это приводит к тому, что Tomcat переходит в кататоническое состояние, когда он не отвечает ни на какие запросы ... даже запросы на начать сначала.)
РЕДАКТИРОВАТЬ
Я не говорю, что ловить OOM - плохая идея. Проблемы возникают, когда вы пытаетесь выйти из OOME намеренно или по недосмотру. Всякий раз, когда вы ловите OOM (напрямую или как подтип Error или Throwable), вы должны либо повторно выбросить его, либо обеспечить выход из приложения / JVM.
Кроме того: это говорит о том, что для максимальной устойчивости перед лицом OOM приложение должно использовать Thread.setDefaultUncaughtExceptionHandler () для установки обработчика, который приведет к завершению приложения в случае OOME, независимо от того, в каком потоке запущено OOME. Мне было бы интересно узнать мнение по этому поводу ...
Единственный другой сценарий - это когда вы точно знаете , что OOM не привел к какому-либо сопутствующему ущербу; т.е. вы знаете:
Есть приложения, в которых можно узнать эти вещи, но для большинства приложений вы не можете точно знать, что продолжение после OOME безопасно. Даже если это эмпирически «работает», когда вы попробуете.
(Проблема в том, что требуется формальное доказательство, чтобы показать, что последствия "ожидаемых" OOME безопасны, и что "непредвиденные" OOME не могут произойти под контролем try / catch OOME.)
источник
Error
.Да, есть сценарии из реальной жизни. Вот мой: мне нужно обработать наборы данных из очень многих элементов в кластере с ограниченной памятью на узел. Отдельные экземпляры JVM проходят через множество элементов один за другим, но некоторые из них слишком велики для обработки в кластере: я могу уловить
OutOfMemoryError
и отметить, какие элементы слишком велики. Позже я могу повторно запустить только большие объекты на компьютере с большим объемом оперативной памяти.(Поскольку происходит сбой при выделении одного массива нескольких гигабайт, JVM по-прежнему работает нормально после обнаружения ошибки, а памяти достаточно для обработки других элементов.)
источник
byte[] bytes = new byte[length]
? Почему бы просто не проверитьsize
на более раннем этапе?size
будет и с большим объемом памяти. Я использую исключение, потому что в большинстве случаев все будет хорошо.Определенно есть сценарии, в которых имеет смысл поймать OOME. IDEA улавливает их и открывает диалоговое окно, позволяющее изменить настройки загрузочной памяти (а затем закрывается, когда вы закончите). Сервер приложений может их поймать и сообщить. Ключом к этому является выполнение этого на высоком уровне отправки, чтобы у вас был разумный шанс освободить кучу ресурсов в точке, где вы перехватываете исключение.
Помимо сценария IDEA, приведенного выше, в общем случае перехват должен быть Throwable, а не только OOM, и должен выполняться в контексте, где, по крайней мере, поток будет вскоре завершен.
Конечно, в большинстве случаев память не хватает, и ситуацию невозможно исправить, но есть способы, которыми это имеет смысл.
источник
Я столкнулся с этим вопросом, потому что мне было интересно, стоит ли ловить OutOfMemoryError в моем случае. Я отвечаю здесь частично, чтобы показать еще один пример, когда отлов этой ошибки может иметь смысл для кого-то (то есть для меня), а частично, чтобы выяснить, действительно ли это хорошая идея в моем случае (поскольку я являюсь uber-младшим разработчиком, я никогда не смогу будьте слишком уверены в каждой строке кода, который я пишу).
В любом случае, я работаю над приложением для Android, которое можно запускать на разных устройствах с разным объемом памяти. Опасная часть - это декодирование растрового изображения из файла и отображение его в экземпляре ImageView. Я не хочу ограничивать более мощные устройства с точки зрения размера декодированного растрового изображения, и я не могу быть уверен, что приложение не будет запускаться на каком-то древнем устройстве, с которым я никогда не сталкивался с очень низкой памятью. Поэтому я делаю это:
Таким образом, мне удается создавать более и менее мощные устройства в соответствии с их потребностями или ожиданиями пользователей.
источник
Да, настоящий вопрос в том, «что вы собираетесь делать в обработчике исключений?» Практически для всего полезного вы выделяете больше памяти. Если вы хотите выполнить некоторую диагностическую работу при возникновении OutOfMemoryError, вы можете использовать
-XX:OnOutOfMemoryError=<cmd>
ловушку, предоставляемую виртуальной машиной HotSpot. Он выполнит ваши команды при возникновении OutOfMemoryError, и вы сможете делать что-нибудь полезное вне кучи Java. Вы действительно хотите, чтобы приложение не исчерпало память, поэтому первый шаг - выяснить, почему это происходит. Затем вы можете при необходимости увеличить размер кучи MaxPermSize. Вот еще несколько полезных ловушек HotSpot:Смотрите полный список здесь
источник
OutOfMemeoryError
может быть брошен в любой момент вашей программы (не только изnew
оператора), ваша программа будет в неопределенном состоянии, когда вы поймаете исключение.У меня есть приложение, которое нужно восстанавливать после сбоев OutOfMemoryError, и в однопоточных программах оно всегда работает, но иногда не работает в многопоточных программах. Приложение представляет собой автоматизированный инструмент тестирования Java, который выполняет сгенерированные тестовые последовательности на максимально возможной глубине в тестовых классах. Теперь пользовательский интерфейс должен быть стабильным, но движку тестирования может не хватить памяти во время роста дерева тестовых случаев. Я справляюсь с этим с помощью следующей идиомы кода в тестовой машине:
Это всегда работает в однопоточных приложениях. Но недавно я поместил свой тестовый движок в отдельный рабочий поток от пользовательского интерфейса. Теперь нехватка памяти может произойти произвольно в любом потоке, и мне непонятно, как ее отловить.
Например, у меня происходило OOME, когда кадры анимированного GIF в моем пользовательском интерфейсе циклически перебирались проприетарным потоком, который создается за кулисами классом Swing, который находится вне моего контроля. Я думал, что выделил все необходимые ресурсы заранее, но очевидно, что аниматор выделяет память каждый раз, когда выбирает следующее изображение. Если у кого-то есть идея о том, как обрабатывать OOME, поднятые в любом потоке, я хотел бы услышать.
источник
OOME можно поймать, но в целом он будет бесполезен, в зависимости от того, может ли JVM собирать мусор для некоторых объектов при достижении улова, и сколько памяти кучи останется к этому времени.
Пример: в моей JVM эта программа выполняется до завершения:
Однако простое добавление единственной строчки в уловке покажет вам, о чем я говорю:
Первая программа работает нормально, потому что при достижении улова JVM обнаруживает, что список больше не будет использоваться (это обнаружение также может быть оптимизацией, сделанной во время компиляции). Итак, когда мы достигаем оператора печати, память кучи была освобождена почти полностью, так что теперь у нас есть большой запас маневра для продолжения. Это лучший случай.
Однако, если код организован так, как если бы список
ll
использовался после того, как OOME был пойман, JVM не сможет его собрать. Это происходит во втором фрагменте. OOME, запускаемый новым созданием Long, улавливается, но вскоре мы создаем новый объект (String вSystem.out,println
строке), а куча почти заполнена, поэтому создается новый OOME. Это наихудший сценарий: мы пытались создать новый объект, у нас ничего не вышло, мы перехватили OOME, да, но теперь первая инструкция, требующая новой памяти кучи (например, создание нового объекта), вызовет новый OOME. Подумайте об этом, что еще мы можем сделать в этот момент, когда осталось так мало памяти? Наверное, просто ухожу. Отсюда и бесполезное.Одна из причин, по которой JVM не собирает ресурсы, действительно пугает: общий ресурс, который также используется другими потоками. Любой, у кого есть мозг, может увидеть, насколько опасным может быть обнаружение OOME, если его вставить в какое-либо неэкспериментальное приложение любого рода.
Я использую 32-битную JVM для Windows x86 (JRE6). Память по умолчанию для каждого приложения Java составляет 64 МБ.
источник
ll=null
внутри блока catch?Единственная причина, по которой я могу думать о том, почему перехват ошибок OOM может заключаться в том, что у вас есть массивные структуры данных, которые вы больше не используете, и вы можете установить значение null и освободить некоторую память. Но (1) это означает, что вы тратите память и вам следует исправить свой код, а не просто хромать после OOME, и (2) что бы вы сделали, даже если вы его поймали? OOM может произойти в любое время, потенциально оставив все наполовину готовым.
источник
По вопросу 2 я уже вижу решение, которое я бы предложил, от BalusC.
Думаю, я только что наткнулся на хороший пример. Когда приложение awt отправляет сообщения, на stderr отображается неотловленная ошибка OutOfMemoryError, и обработка текущего сообщения останавливается. Но приложение продолжает работать! Пользователь по-прежнему может выполнять другие команды, не зная о серьезных проблемах, происходящих за сценой. Особенно, когда он не может или не соблюдает стандартную ошибку. Так что перехват исключения oom и обеспечение (или, по крайней мере, предложение) перезапуска приложения - это то, что нужно.
источник
У меня просто есть сценарий, в котором обнаружение OutOfMemoryError имеет смысл и, похоже, работает.
Сценарий: в приложении для Android я хочу отображать несколько растровых изображений с максимально возможным разрешением, и я хочу иметь возможность плавно их масштабировать.
Из-за плавного масштабирования я хочу, чтобы растровые изображения были в памяти. Однако у Android есть ограничения в памяти, которые зависят от устройства и которые трудно контролировать.
В этой ситуации при чтении растрового изображения может возникнуть ошибка OutOfMemoryError. Здесь помогает, если я поймаю это, а затем продолжу с более низким разрешением.
источник
OutOfMemory
этого не происходит из-за несвязанного исправления). Однако, даже если вы поймаете это, он все равно может сломать какой-то важный код: если у вас несколько потоков, выделение памяти может завершиться ошибкой в любом из них. Таким образом, в зависимости от вашего приложения вероятность необратимого выхода из строя составляет 10-90%.РЕДАКТИРОВАТЬ: Я предлагаю вам попробовать. Скажем, напишите программу, которая рекурсивно вызывает функцию, которая постепенно выделяет больше памяти. Поймайте
OutOfMemoryError
и посмотрите, сможете ли вы осмысленно продолжить с этого момента. По моему опыту, вы сможете это сделать, хотя в моем случае это произошло на сервере WebLogic, так что здесь могла быть какая-то черная магия.источник
Вы можете поймать что угодно в Throwable, вообще говоря, вы должны ловить только подклассы Exception, исключая RuntimeException (хотя большая часть разработчиков также ловит RuntimeException ... но это никогда не было намерением разработчиков языка).
Если бы вы поймали OutOfMemoryError, что бы вы сделали? На виртуальной машине не хватает памяти, все, что вы можете сделать, это выйти. Вы, вероятно, даже не можете открыть диалоговое окно, чтобы сказать им, что у вас не хватает памяти, поскольку это займет память :-)
Виртуальная машина выдает OutOfMemoryError, когда действительно не хватает памяти (действительно, все ошибки должны указывать на неисправимые ситуации), и вы действительно ничего не можете сделать, чтобы с этим справиться.
Что нужно сделать, это выяснить, почему вам не хватает памяти (используйте профилировщик, такой как в NetBeans), и убедитесь, что у вас нет утечек памяти. Если у вас нет утечек памяти, увеличьте объем памяти, который вы выделяете виртуальной машине.
источник