Может JVM восстанавливаться с OutOfMemoryError без перезапуска

  1. JVM может восстановиться с OutOfMemoryError без перезапуска, если это получает шанс выполнить GC, прежде чем больше объектных запросов выделения войдет?

  2. Различные реализации JVM отличаются по этому аспекту?

Мой вопрос о восстановлении JVM а не пользовательской программе, пытающейся восстановиться путем фиксации ошибки. Другими словами, если OOME брошен в сервер приложений (jboss/websphere/..) я должен перезапустить его? Или я могу позволить ему работать, если дальнейшие запросы, кажется, работают без проблемы.

43
задан Joachim Sauer 12 September 2013 в 08:15
поделиться

6 ответов

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

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

  • OOME можно бросить в любую ветку. Если поток приложения или библиотека не предназначены для работы с ним, это может оставить некоторую долговечную структуру данных в неполном или несогласованном состоянии.

  • Если потоки умирают в результате OOME, приложению может потребоваться перезапустить их как часть восстановления OOME. По крайней мере, это усложняет приложение.

  • Предположим, что поток синхронизируется с другими потоками с помощью уведомления / ожидания или какого-либо механизма более высокого уровня. Если этот поток умирает из-за OOME, другие потоки могут остаться в ожидании уведомлений (и т. Д.), Которые никогда не приходят ... например. Разработка для этого может значительно усложнить приложение.

Таким образом, проектирование, реализация и тестирование приложения для восстановления из OOME могут быть трудными, особенно если приложение (или структура, в которой оно выполняется, или любая из библиотек, которые оно использует) является многопоточным. Лучше рассматривать OOME как фатальную ошибку.

См. Также мой ответ на связанный вопрос:

РЕДАКТИРОВАТЬ - в ответ на следующий вопрос:

Другими словами, если OOME создается на сервере приложений (jboss / websphere / ..) мне нужно перезапустить его?

Нет, вы не должны перезапустить. Но это, вероятно, мудро , особенно если у вас нет хорошего / автоматического способа проверки правильности работы службы.

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

EDIT 2

В ответ на этот комментарий:

«другие потоки могут остаться в ожидании уведомлений (и т. Д.), Которые никогда не приходят» Правда? Разве убитый поток не раскрутил бы свои стеки, высвобождая ресурсы по ходу, включая удерживаемые блокировки?

Да уж! Рассмотрим это:

Поток №1 выполняет это:

    synchronized(lock) {
         while (!someCondition) {
             lock.wait();
         }
    }
    // ...

Поток №2 выполняет следующее:

    synchronized(lock) {
         // do stuff
         lock.notify();
    }

Если поток №1 ожидает уведомления, а поток №2 получает сообщение OOME в // сделайте что-нибудь , то поток №2 не будет вызывать notify () , а поток №1 может навсегда застрять в ожидании уведомления, которого никогда не будет. Конечно, поток № 2 гарантированно освободит мьютекс для объекта lock ... но этого недостаточно!

В противном случае код, выполняемый потоком, не является безопасным в отношении исключений, что является более общей проблемой.

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

Вам понадобится какой-то механизм, посредством которого сбой потока №1 (из-за OOME) превращается в уведомление об ошибке межпотоковой связи для потока №2. Erlang делает это ... но не Java.Причина, по которой они могут делать это в Erlang, заключается в том, что процессы Erlang взаимодействуют с использованием строгих примитивов, подобных CSP; т.е. отсутствует совместное использование структур данных!

(Обратите внимание, что указанная выше проблема может возникнуть практически для любого неожиданного исключения ... а не только для исключений Error . Существуют определенные типы кода Java, в которых выполняется попытка восстановления после непредвиденное исключение , вероятно, закончится плохо.)

44
ответ дан 26 November 2019 в 23:04
поделиться

Вы можете увеличить свои шансы на восстановление после этого сценария, хотя это не рекомендуется вам пытаться. Что вы делаете, так это предварительно выделяете некоторый фиксированный объем памяти при запуске, который предназначен для выполнения вашей работы по восстановлению, а когда вы ловите OOM, обнуляете эту предварительно выделенную ссылку, и у вас больше , вероятно, будет немного памяти для использования в процессе восстановления.

Я не знаю о различных реализациях JVM.

1
ответ дан 26 November 2019 в 23:04
поделиться

JVM запустит GC, когда окажется на грани OutOfMemoryError. Если GC ничем не помог, то JVM выбросит OOME.

Вы можете, однако, поймать его и, если необходимо, пойти альтернативным путем. Любое выделение внутри блока try будет GC'ed.

Поскольку OOME - это "просто" ошибка, которую вы можете просто поймать, я ожидаю, что различные реализации JVM будут вести себя одинаково. По крайней мере, я могу подтвердить на собственном опыте, что вышесказанное верно для Sun JVM.

См. также:

3
ответ дан 26 November 2019 в 23:04
поделиться

Я бы сказал, что это частично зависит от того, что вызвало OutOfMemoryError. Если JVM действительно не хватает памяти, было бы неплохо перезапустить ее, и по возможности с большим объемом памяти (или с более эффективным приложением). Однако я видел достаточно много OOME, которые были вызваны выделением массивов по 2 ГБ и тому подобными проблемами. В этом случае, если это что-то вроде веб-приложения J2EE, последствия ошибки должны быть ограничены этим конкретным приложением, и перезапуск всей JVM не принесет никакой пользы.

3
ответ дан 26 November 2019 в 23:04
поделиться

Любая нормальная JVM выдаст OutOfMemoryError только в том случае, если сборщик мусора ничего не может сделать. Однако, если вы поймаете OutOfMemoryError достаточно рано в кадре стека, вполне вероятно, что причина сама стала недоступной и была собрана мусором (если проблема не в текущем потоке).

Обычно фреймворки, которые запускают другой код, например серверы приложений, пытаются продолжить работу перед лицом OME (при условии, что они могут разумно освободить сторонний код), но в противном случае, в общем случае, восстановление должно вероятно, состоит из спасения и объяснения пользователю причины, а не из попытки продолжать, как будто ничего не произошло.

Чтобы ответить на ваш недавно обновленный вопрос: нет причин думать, что вам нужно выключить сервер, если все работает нормально. Мой опыт работы с JBoss показывает, что пока OME не влияет на развертывание, все работает нормально. Иногда JBoss исчерпывает пространство для перманента, если вы выполняете много горячего развертывания. Тогда действительно ситуация безнадежна, и немедленный перезапуск (который придется принудительно убить) неизбежен.

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

1
ответ дан 26 November 2019 в 23:04
поделиться

Может восстановить ? Возможно. Любая хорошо написанная JVM выбросит OOME только после того, как испробует все возможное, чтобы освободить достаточно памяти, чтобы делать то, что вы ей говорите. Есть очень хороший шанс, что это означает, что вы не сможете выздороветь. Но ...

Это зависит от многих вещей. Например, если сборщик мусора не является копирующим сборщиком, условием «нехватки памяти» на самом деле может быть «не осталось достаточно большого фрагмента для выделения». Сам процесс раскрутки стека может привести к очистке объектов в более позднем раунде сборки мусора, в результате чего открытые фрагменты останутся достаточно большими для ваших целей. В этой ситуации вы можете перезапустить. В результате, вероятно, стоит хотя бы один раз повторить попытку. Но ...

Вы, наверное, не хотите полагаться на это. Если вы регулярно получаете OOME, вам лучше просмотреть свой сервер и выяснить, что происходит и почему. Возможно, вам нужно очистить свой код (возможно, вы протекаете или создаете слишком много временных объектов). Возможно, вам придется поднять потолок памяти при вызове JVM. Относитесь к OOME, даже если его можно восстановить, как к признаком того, что что-то плохое поразило вентилятор где-то в вашем коде, и действуйте соответственно.Возможно, ваш сервер не должен отключаться СЕЙЧАС, но вам придется что-то исправить, прежде чем вы попадете в более серьезную проблему.

2
ответ дан 26 November 2019 в 23:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: