Всякий раз, когда вы обнаруживаете, что запускаете трассировку стека с OutOfMemoryError, все должно быть кристально чистым. У программы больше нет свободного места и она умирает только из-за ее отсутствия. С 10 000 футов или с кресла руководителя это может уже содержать слишком много информации. Но те из вас, кому необходимо создавать или поддерживать приложения и выяснить, почему возникает конкретная ошибка, — мы можем немного подробнее разобраться в проблеме.
В этом посте мы рассмотрим, что на самом деле означают разные сообщения OutOfMemoryError. Мы начнем с самых распространенных случаев и перейдем к более интересным ситуациям.
- java.lang.OutOfMemoryError: пространство кучи Java
- java.lang.OutOfMemoryError: пространство PermGen
- java.lang.OutOfMemoryError: Превышен лимит накладных расходов GC
- java.lang.OutOfMemoryError: невозможно создать новый собственный поток
- java.lang.OutOfMemoryError: nativeGetNewTLA
- java.lang.OutOfMemoryError: размер запрашиваемого массива превышает ограничение виртуальной машины
- java.lang.OutOfMemoryError: запросить <размер> байтов для <причина>. Вне пространства подкачки?
- java.lang.OutOfMemoryError: <причина> <трассировка стека> (собственный метод)
java.lang.OutOfMemoryError: пространство кучи Java . Мы начнем с того, кого вы все видели больше, чем вам бы хотелось. Это способ виртуальной машины Java объявить вам, что в области кучи виртуальной машины больше нет места. Вы пытаетесь создать новый объект, но объем памяти, которую эта вновь созданная структура собирается использовать, больше, чем у JVM в куче. JVM пыталась освободить память, вызывая полный GC, прежде чем бросить полотенце, но безуспешно. Самый быстрый способ избавиться от симптомов — увеличить кучу с помощью параметра -Xmx . Обратите внимание, что ни те, ни другие рекомендации, приведенные в статье, должны быть взяты с добавлением соли. Чаще всего вы просто скрываете симптомы основной проблемы.
Следующий подозреваемый также довольно распространен. Я предполагаю, что большинство из вас видели пространство java.lang.OutOfMemoryError: PermGen во время повторных развертываний . Это почти то же самое сообщение, что и первое, но вместо кучи вы сейчас пытаетесь выделить память в области Permanent Generation . И опять же, вам не хватает места, поэтому собственный код JVM достаточно любезен, чтобы сообщить вам об этом. Это сообщение имеет тенденцию исчезать (на некоторое время), если вы увеличиваете параметр -XX: MaxPermSize .
Третий — java.lang.OutOfMemoryError: превышен лимит накладных расходов GC — немного другой зверь. Вместо отсутствующей кучи / permgen JVM сигнализирует о том, что ваше приложение тратит слишком много времени на сборку мусора и мало что показывает для этого. По умолчанию JVM настроен на выдачу этой ошибки, если вы тратите более 98% общего времени в GC, а после GC восстанавливается менее 2% кучи. Похоже, это очень хорошее место, чтобы на месте была надежная защита. В редких случаях, когда имеет смысл отключить его, добавьте -XX: -UseGCOverheadLimit в свои сценарии запуска.
Первые три сообщения OutOfMemoryError выполняют до 98% случаев, которые мы решаем с помощью Plumbr . Так что есть большая вероятность, что оставшееся трио вам неизвестно.
java.lang.OutOfMemoryError: неспособность создать новый собственный поток — это сообщение, которое вы получите, если JVM запрашивает новый поток из ОС, а базовая ОС больше не может выделять новый поток. Этот предел очень зависит от платформы, поэтому, если вам интересно узнать свои ограничения, проведите свой собственный маленький эксперимент, используя следующий фрагмент кода. На моем 64-битном MacOS X, на котором установлена последняя версия JDK 7, у меня возникают проблемы при создании потока №2032.
1
2
3
4
5
6
7
8
9
|
while ( true ){ new Thread( new Runnable(){ public void run() { try { Thread.sleep( 10000000 ); } catch (InterruptedException e) { } } }).start(); } |
java.lang.OutOfMemoryError: nativeGetNewTLA — это признак того, что JVM не может выделить новую локальную область потока . Это то, с чем вы можете столкнуться только на виртуальной машине jRockit. Если вы помните, локальная область потока — это буфер, используемый для эффективного распределения памяти в многопоточном приложении. Каждый поток имеет свой собственный предварительно выделенный буфер, в котором создаются все объекты, созданные этим потоком. Вы столкнетесь с проблемами при создании большого количества объектов в многопоточном приложении, в случае чего вы можете обратиться к настройке параметра -XXtlaSize .
java.lang.OutOfMemoryError: Запрашиваемый размер массива превышает лимит виртуальной машины — это сообщение, на которое вы смотрите, когда пытаетесь создать массив, размер которого больше, чем позволяют ограничения вашей виртуальной машины. На моей 64-битной Mac OS X с недавней сборкой JDK 7 я осознаю тот факт, что массивы с элементами Integer.MAX_INT-2 в порядке, но если еще одна капля, а именно Integer.MAX_INT-1, ломает спину верблюда. На старых 32-битных машинах это имело свои преимущества, ограничивая размеры массивов, чтобы вписаться в крошечные кучи, доступные тогда. На современных 64-битных машинах это создает больше путаницы, чем на самом деле помогает решить что-либо.
java.lang.OutOfMemoryError: запросить <размер> байтов для <причина>. Вне пространства подкачки? Это сообщение об ошибке выдается, когда JVM не может выделить собственную память из ОС. Обратите внимание, что он полностью отличается от стандартных случаев, когда вы исчерпали пространство кучи или permgen. Это сообщение имеет тенденцию отображаться, когда вы работаете вблизи пределов платформы. Поскольку само сообщение гласит, что вы, возможно, превысили объем доступной физической и виртуальной памяти. Поскольку последнее часто реализуется с помощью подкачки памяти на диск, первое, о чем вы могли бы подумать как о быстром исправлении, было бы увеличение размера файла подкачки. Но мне еще предстоит увидеть приложение, которое будет нормально работать при замене, так что, скорее всего, это быстрое исправление вам мало поможет.
java.lang.OutOfMemoryError: <причина> <трассировка стека> (собственный метод) Теперь пришло время попросить помощи у ваших коллег-разработчиков на Си. Как говорится в сообщении, вы сталкиваетесь с проблемами из собственного кода, но в отличие от последнего случая ошибка выделения была обнаружена в JNI или в собственном методе вместо кода JVM.
Все рекомендации, приведенные в статье, должны быть взяты с крошкой соли. Чаще всего вы просто скрываете симптомы основной проблемы. Если вы хотите убедиться, что эти сообщения не были вызваны утечкой памяти, скачайте и попробуйте Plumbr бесплатно.