Может ли JVM восстановиться после ошибки OutOfMemoryError без перезагрузки - PullRequest
43 голосов
/ 17 июня 2010
  1. Может ли JVM восстановиться после ошибки OutOfMemoryError без перезапуска, если она получит возможность запустить GC до того, как поступит больше запросов на выделение объектов?

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

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

Ответы [ 6 ]

43 голосов
/ 17 июня 2010

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

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

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

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

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

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

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

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

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

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

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

РЕДАКТИРОВАТЬ 2

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

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

Да, действительно! Учтите это:

Тема # 1 запускает это:

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

Тема # 2 запускает это:

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

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

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

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

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

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

3 голосов
/ 17 июня 2010

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

3 голосов
/ 17 июня 2010

JVM будет запускать ГХ, когда он находится на краю OutOfMemoryError. Если сборщик мусора вообще не помог, JVM выдаст OOME.

Вы можете однако catch его и при необходимости выберите альтернативный путь. Все выделения внутри блока try будут помечены GC.

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

Смотри также:

2 голосов
/ 17 июня 2010

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

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

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

1 голос
/ 17 июня 2010

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

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

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

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

1 голос
/ 17 июня 2010

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

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

...