Исключения java нехватка памяти

Обновлено: 06.07.2024

Как лучше всего отлаживать исключения java.lang.OutOfMemoryError ?

Когда это происходит с нашим приложением, наш сервер приложений (Weblogic) генерирует файл дампа кучи. Стоит ли использовать файл дампа кучи? Должны ли мы создавать дамп потока Java? В чем именно разница?

Обновление: как лучше всего создавать дампы потоков? Является ли kill -3 (наше приложение работает на Solaris) лучшим способом убить приложение и сгенерировать дамп потока? Есть ли способ сгенерировать дамп потока, но не убить приложение?

Анализировать и исправлять ошибки нехватки памяти в Java очень просто.

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

Шаг 1. Включите дампы кучи во время выполнения

Запустите процесс с помощью -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

(Это безопасно, чтобы эти параметры всегда были включены. При необходимости измените путь, он должен быть доступен для записи пользователем java)

Шаг 2. Воспроизведите ошибку

Дайте приложению работать до тех пор, пока не появится OutOfMemoryError .

JVM автоматически запишет файл типа java_pid12345.hprof .

Шаг 3. Получите дамп

Скопируйте java_pid12345.hprof на свой компьютер (он будет не меньше вашего максимального размера кучи, поэтому может стать довольно большим - при необходимости заархивируйте его).

Шаг 4. Откройте файл дампа с помощью Heap Analyzer или Eclipse Анализатор памяти

Анализатор кучи представит вам дерево всех объектов, которые были активны на момент ошибки. Скорее всего, при открытии он укажет вам прямо на проблему.

IBM HeapAnalyzer

Примечание: дайте HeapAnalyzer достаточно памяти, так как он должен загрузить весь ваш дамп!

Шаг 5. Определите области наибольшего использования кучи

Просмотрите дерево объектов и определите объекты, которые хранятся без надобности.

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

Шаг 6. Исправьте свой код

Убедитесь, что вы храните вокруг только те предметы, которые вам действительно нужны. Своевременно удаляйте предметы из коллекций. Убедитесь, что не храните ссылки на объекты, которые больше не нужны, только тогда они могут быть удалены сборщиком мусора.

Мне удалось успешно использовать комбинацию Eclipse Memory Analyzer (MAT) и Java Visual VM, чтобы анализировать дампы кучи. В MAT есть несколько отчетов, которые вы можете запустить и которые дадут вам общее представление о том, на чем следует сосредоточить свои усилия в вашем коде. VisualVM имеет лучший интерфейс (на мой взгляд) для проверки содержимого различных объектов, которые вы хотите изучить. У него есть фильтр, с помощью которого вы можете отобразить все экземпляры определенного класса и увидеть, где на них есть ссылки и на что они ссылаются сами. Прошло некоторое время с тех пор, как я использовал для этого любой инструмент, теперь у них может быть более близкий набор функций. В то время использование обоих подходило мне.

Как лучше всего отлаживать исключения java.lang.OutOfMemoryError ?

Существуют различные основные причины исключений нехватки памяти. Дополнительные сведения см. На странице документации Oracle. .

java.lang.OutOfMemoryError: Java heap space :

java.lang.OutOfMemoryError: GC Overhead limit exceeded :

java.lang.OutOfMemoryError: Requested array size exceeds VM limit :

Причина . Метаданные класса Java (внутреннее представление класса Java виртуальными машинами) размещены в собственной памяти (называемой здесь метапространством)

java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space? :

java.lang.OutOfMemoryError: Compressed class space

Причина . На 64-битных платформах указатель на метаданные класса может быть представлен 32-битным смещением (с UseCompressedOops). Это контролируется флагом командной строки UseCompressedClassPointers (по умолчанию включен).

Если используется UseCompressedClassPointers , объем доступного пространства для метаданных класса фиксируется на уровне CompressedClassSpaceSize . Если пространство, необходимое для UseCompressedClassPointers , превышает CompressedClassSpaceSize , создается java.lang.OutOfMemoryError с подробным описанием сжатого пространства класса.

Примечание . Существует более одного типа метаданных класса - метаданные класса и другие метаданные. В пространстве, ограниченном CompressedClassSpaceSize , хранятся только метаданные класса. Остальные метаданные хранятся в Metaspace.

Стоит ли использовать файл дампа кучи? Должны ли мы создавать дамп потока Java? В чем именно разница?

Да. Вы можете использовать этот файл дампа кучи для отладки проблемы с помощью инструментов профилирования, таких как visualvm или mat Вы можете использовать дамп потока, чтобы получить более полное представление о состоянии потоков.

Обратитесь к этому вопросу SE, чтобы узнать различия:

Как лучше всего создавать дампы потоков? Является ли kill -3 (наше приложение работает на Solaris) лучшим способом убить приложение и сгенерировать дамп потока? Есть ли способ сгенерировать дамп потока, но не убить приложение?

kill -3 <process_id> генерирует дамп потока, и эта команда не убивает java-процесс.

Обычно очень сложно отлаживать проблемы OutOfMemoryError . Я бы рекомендовал использовать инструмент профилирования. JProfiler работает неплохо. Я использовал его в прошлом, и он может быть очень полезным, но я уверен, что есть и другие, которые не менее хороши.

Чтобы ответить на ваши конкретные вопросы:

Дамп кучи - это полное представление всей кучи, то есть всех объектов, которые были созданы с помощью new . Если у вас заканчивается память, она будет довольно большой. Он показывает, сколько у вас есть предметов каждого типа.

Дамп потока показывает стек для каждого потока, показывая, где в коде каждый поток находится во время дампа. Помните, что любой поток мог вызвать нехватку памяти JVM, но это может быть другой поток, который на самом деле вызывает ошибку. Например, поток 1 выделяет массив байтов, который заполняет все доступное пространство кучи, затем поток 2 пытается выделить 1-байтовый массив и выдает ошибку.

Вы также можете использовать jmap / jhat для присоединения к запущенному процессу Java. Эти (семейство) инструментов действительно полезны, если вам нужно отлаживать работающее приложение в реальном времени.

Вы также можете оставить jmap запущенным как задачу cron, регистрирующуюся в файле, который вы можете проанализировать позже (это то, что мы сочли полезным для отладки утечки оперативной памяти)

Jmap также можно использовать для создания дампа кучи с помощью параметра -dump, который можно прочитать через jhat.

Основные симптомы утечек памяти Java

Встречаются несколько симптомов указывающих, что приложение имеет проблемы утечки памяти. Небольшое снижение производительности, а не внезапный отказ работы приложения, указывает только на утечку памяти. Проблема может случаться каждый раз во время работы или только тогда, когда приложение начинает работать с большим объемом данных или, напротив, вы начинаете масштабировать приложение. Приложение, возможно, покажет ошибку нехватки памяти, как только утечка сожрет все доступные ресурсы памяти. Перезапустив приложение и надеясь на лучшее, натолкнетесь неоднократно на аварийные завершения до того момента, пока утечка не будет устранена. В общем, утечки памяти случаются когда ссылки на объекты накапливаются, вместо того, чтобы освобождать память. Они занимают всю доступную память и делают для приложения невозможным доступ до нужных ему ресурсов.

Ошибки конфигурации выглядящие как утечки памяти

Перед тем, как заглянете в ситуации вызывающие проблемы с памятью Java и проведете анализ, необходимо убедиться, что исследования не имеют отношения к абсолютно другой задаче. Часть ошибок out-of-memory возникают из-за различных ошибок, например ошибок конфигурации. У приложения, возможно, недостаток памяти в куче или оно конфликтует в системе с другими приложениями. Если начинаете говорить о проблемах нехватки памяти, но не можете определить что вызывает утечку, взгляните на приложение по-другому. Обнаружится, что нужно сделать изменения в потоке финализации или увеличить объем permanent generation пространства, являющегося областью памяти JVM для хранения описания классов Java и некоторых дополнительных данных.

Преимущества инструментов мониторинга памяти

Инструменты мониторинга памяти дают бОльшую видимость использования доступных ресурсов приложением Java. Используя данное ПО, вы делаете шаг для сужения поиска корня проблемы утечки памяти и прочих инцидентов связанных с производительностью. Инструменты идут в нескольких категориях, и вам, возможно, нужно использовать множество приложений, чтобы разобраться как начать правильно обозначать проблему и что пошло не так, даже если вы имеете дело с утечками памяти. Heap dump (дампа кучи) файлы дают необходимые сведения для анализа Java-памяти. В этом случае вам нужно использовать два инструмента: один для генерации дамп-файла и другой для подробного анализа. Такое решение дает детализированную информацию о том, что происходит с приложением. Один раз инструмент указывает места возможных проблем и работает над сужением площади, чтобы обнаружить точное место возникновения инцидента. И этот период времени - время самой длинной и портящей настроение части проб и ошибок. Анализатор памяти указывает несколько проблем в коде, но вы не уверены абсолютно, с какими проблемами столкнулось ваше приложение. Если всё ещё сталкиваетесь с прежней ошибкой, начните сначала и поработайте над другой возможной ошибкой. Сделайте одно изменение за раз и попытайтесь продублировать ошибку. Нужно будет дать приложению поработать некоторое время, чтобы продублировать условия возникновения ошибки. Если при первом тесте происходит утечка памяти, не забудьте протестировать приложение под нагрузкой. Приложение может работать отлично с небольшим количеством данных, но может снова выбросить прежние ошибки при работе с большим объемом данных. Если еще возникает всё та же самая ошибка, нужно начать сначала и разобрать другую возможную причину. Инструменты мониторинга памяти доказывают свою пользу после того, когда приложение стало полностью работающим. Можно удаленно наблюдать за производительностью JVM и проактивным обнаружением сбойных ситуаций перед тем, как разработчик погрузится в проблему и будет собирать исторические данные производительности, чтобы помочь себе в будущем улучшить техники программирования и наблюдать как Java работает под тяжелой нагрузкой. Многие решения включают режимы оповещения "опасность" или другие подобные режимы и разработчик сразу может знать, что происходит не так, как хотелось. Каждый разработчик не хочет, чтобы критическое приложение, будучи в промэксплуатации, падало и являлось причиной потери десятков или сотен тысяч долларов во время простоя приложения, поэтому инструменты мониторинга памяти уменьшают время реагирования разработчика. Приложения мониторинга памяти дают начать процесс диагностики мгновенно, вместо того, чтобы попросить вас пойти к заказчику, где никто не скажет какая именно ошибка случилось или какой код ошибки выдало приложение. Если часто погружаетесь в проблемы памяти и производительности вашего Java-приложения, плотно возьмитесь за процесс тестирования. Обозначьте каждую слабую область в процессе разработки и измените стратегии тестирования. Посоветуйтесь с коллегами и сравните свои подходы тестирования с существующими лучшими практиками. Иногда вам надо пересмотреть маленький фрагмент кода и далее обеспечить длительное воздействие на все приложение.

Роль Garbage Collector на память Java и утечки памяти

Garbage Collector (cборщик мусора) в Java играет ключевую роль в производительности приложения и использования памяти. Он ищет неиспользуемые (мертвые) объекты и удаляет их. Эти объекты больше не занимают память, так что ваше приложение продолжает обеспечивать доступность ресурсов. Иногда приложение не дает GC достаточно времени или ресурсов для удаления мертвых объектов и они накапливаются. Можно столкнуться с такой ситуацией когда идет активное обращение к объектам, которые, вы полагаете, мертвы. Сборщик мусора не может сделать ничего c этим, т.к. его механизм автоматизированного управления памяти обходит активные объекты. Обычно сборщик мусора работает автономно, но необходимо настроить его поведение на реагирование тяжелых проблем с памятью. Однако, GC может сам приводить к проблемам производительности.

Области GC

Сборщик мусора для оптимизации сборки разделяет объекты на разные области. В Young Generation представлены объекты, которые отмирают быстро. Сборщик мусора часто работает в этой области, с того момента, когда он должен проводить очистку. Объекты оставшиеся живыми по достижению определенного периода переходят в Old Generation. В области Old Generation объекты остаются долгое время, и они не удаляются сборщиком так часто. Однако, когда сборщик работает в области, приложение проходит проходит через большую операцию, где сборщик смотрит сквозь живые объекты для очистки мусора. В итоге объекты приложения находятся в конечной области permanent generation. Обычно, эти объекты включают нужные метаданные JVM. Приложение не генерирует много мусора в Permanent Generation, но нуждается в сборщике для удаления классов когда классы больше не нужны.

Связь между Garbage Collector и временем отклика

Сборщик мусора, независимо от приоритета исполнения потоков приложения, останавливает их не дожидаясь завершения. Такое явление называется событием "Stop the World". Область Young Generation сборщика мусора незначительно влияет на производительность, но проблемы заметны, если GC выполняет интенсивную очистку. В конечном итоге вы оказываетесь в ситуации, когда минорная сборка мусора Young Generation постоянно запущена или Old Generation переходит в неконтролируемое состояние. В такой ситуации нужно сбалансировать частоту Young Generation с производительностью, которая требует увеличение размера этой области сборщика. Области Permanent Generation и Old Generation сборщика мусора значительно влияют на производительность приложения и использования памяти. Эта операция major очистки мусора проходит сквозь heap, чтобы вытолкнуть отмершие объекты. Процесс длится дольше чем minor сборка и влияние на производительность может идти дольше. Когда высокая интенсивность очистки и большой размер области Old Generation, производительность всего приложения увязывает из-за событий "Stop the world". Оптимизация сборки мусора требует мониторинга как часто программа запущена, влияния на всю производительность и способов настройки параметров приложения для уменьшения частоты мониторинга. Возможно нужно будет идентифицировать один и тот же объект, размещенный больше, чем один раз, причем приложению не нужно отгораживаться от размещения или вам надо найти точки сжатия, сдерживающие всю систему. Получение правильного баланса требует уделения близкого внимания ко всему от нагрузки на CPU до циклов вашего сборщика мусора, особенно если Young и Old Generation несбалансированы. Адресация утечек памяти и оптимизация сборки мусора помогает увеличить производительность Java-приложения. Вы буквально жонглируете множеством движущихся частей. Но с правильным подходом устранения проблем и инструментами анализа, спроектированных чтобы дать строгую видимость, вы достигнете света в конце туннеля. В противном случае замучаетесь с возникающими неполадками связанных с произодительностью. Тщательное размещение памяти и её мониторинг играют критическую роль в Java-приложении. Необходимо полностью взять в свои руки взаимодействие между сборкой мусора, удалением объектов и производительностью, чтобы оптимизировать приложение и избежать ошибок упирающихся в нехватку памяти. Инструменты мониторинга дают оставаться на высоте, чтобы обнаружить возможные проблемы и обозначить тенденции утилизации памяти так, что вы принимаете проактивный подход к исправлению неисправностей. Утечки памяти часто показывают неэффективность устранения неисправностей обычным путем, особенно если вы сталкиваетесь с неверными значениями параметров конфигурации, но решения вопросов связанных с памятью помогают быстро избежать инцидентов стоящих у вас на пути. Совершенство настройки памяти Java и GC делают ваш процесс разработки намного легче.



xms и xmx — это про кучу, а тебе скорее стека не хватит, если ты по 1000 потоков стартуешь.



Скорее всего наступил на лимит числа потоков для пользователя, подними его в limits.conf.



Lordwind ★★★★★ ( 20.06.18 13:34:19 )
Последнее исправление: Lordwind 20.06.18 13:34:44 (всего исправлений: 1)


Смотри стек откуда вываливается, если возможно то сними дамп (и кол-во потоков) перед ошибкой.

Похоже, что у тебя там 100500 потоков (это или утечка, или кривая архитектура). А память не при чём.


Если не x86/64- то вероятнее всего кривой pthread на архитектуре(особенно если собран clang-ом)

если x86/64 потыкай Оракловскую JRE

таки да больше всего похоже на какойто лимит железный/ядра/библиотек/пользовательыских прав(типа cgroup), а не самой джавы


Забыл прописал одновременно софт и хард лимиты. Дописал до 65000, не помогло.


Где-то около 800 потоков жава добирает памяти до 3.6 Гб и заметно позже обычного вываливает несколько таких ошибок. Я уж подумал что случайно запустил в 32-бит, но нет

Как так может быть? Процесс 64 бит, а ведет себя как 32 бит О_о


Потести на GhostBSD. Там и OpenJDK8 с JMeter должны быть посвежее.


Со своей машины я проверить не смогу: Нагрузочное тестирование через VPN

Хотя по недавнему опыту jmeter у меня и 10к соединений держал, вроде на десктопной убунте. Сейчас доступна только амазоновская.


попробуй понизить xss, например если ты сделаешь -Xss256k, то треды будут занимать всего 800*256K=

200 мегабайт (скорей всего). Чем меньше значение, тем больше тредов можно создать, но начиная с какого-то числа приложухе настанет карачун

заметь что это будет память не в куче, а внешняя. То есть, если ты установишь xmx xms так, что они сожрут всю память, то на создание тредов не останется вообще ничего.

В Java все объекты хранятся в куче. Они размещены с использованием нового оператора. Исключение OutOfMemoryError в Java выглядит следующим образом:

Обычно эта ошибка выдается, когда виртуальная машина Java не может выделить объект, потому что ему не хватает памяти, и сборщик мусора не может выделить больше памяти.

OutOfMemoryError обычно означает, что вы что-то делаете неправильно, либо слишком долго держитесь за объекты, либо пытаетесь обрабатывать слишком много данных за раз. Иногда это указывает на проблему, которая находится вне вашего контроля, например на стороннюю библиотеку, которая кэширует строки, или на сервере приложений, который не очищается после развертывания. И иногда это не имеет ничего общего с объектами в куче.

Исключение java.lang.OutOfMemoryError также может быть вызвано собственным библиотечным кодом, когда не удается удовлетворить собственное распределение (например, если пространство подкачки мало). Давайте разберемся в различных случаях, когда может возникнуть ошибка OutOfMemory.

Симптом или первопричина?

// Java-программа для иллюстрации
// Ошибка кучи

public class Heap

static List<String> list = new ArrayList<String>();

public static void main(String args[]) throws Exception

Integer[] array = new Integer[ 10000 * 10000 ];

Когда вы выполните приведенный выше код, вы можете ожидать, что он будет работать без проблем. В результате, с течением времени, при постоянно используемом коде утечки, «кэшированные» результаты в итоге занимают много места в куче Java, и когда утечка памяти заполняет всю доступную память в области кучи, и сборщик мусора не может очистите его, java.lang.OutOfMemoryError: выбрасывается пространство кучи Java .

Предотвращение: Проверьте, как отслеживать объекты, для которых завершение ожидает, в разделе Мониторинг объектов, ожидающих завершение .

// Java-программа для иллюстрации
// превышен лимит GC

public class Wrapper

public static void main(String args[]) throws Exception

Map m = new HashMap();

Random r = new Random();

Если вы запустите эту программу с java -Xmx100m -XX: + UseParallelGC Wrapper, то результат будет примерно таким:

Предупреждение: Увеличьте размер кучи и выключите его с помощью флага командной строки -XX: -UseGCOverheadLimit.

// Java-программа для иллюстрации
// Ошибка Пермгенского пространства

public class Permgen

static ClassPool classPool = ClassPool.getDefault();

public static void main(String args[]) throws Exception

for ( int i = 0 ; i < 1000000000 ; i++)

Class c = classPool.makeClass(com.saket.demo.Permgen" + i).toClass();

В приведенном выше примере кода код повторяется в цикле и генерирует классы во время выполнения. Сложность создания классов решается библиотекой Javassist .
Выполнение приведенного выше кода будет продолжать генерировать новые классы и загружать их определения в пространство Permgen, пока пространство не будет полностью использовано и не будет выброшено пространство Permgen java.lang.OutOfMemoryError:
Предотвращение: когда OutOfMemoryError из-за исчерпания PermGen вызывается во время запуска приложения, решение простое. Приложению просто нужно больше места для загрузки всех классов в область PermGen, поэтому нам просто нужно увеличить его размер. Для этого измените конфигурацию запуска приложения и добавьте (или увеличьте, если присутствует) параметр -XX: MaxPermSize, как показано в следующем примере:

// Java-программа для иллюстрации
// Ошибка метапространства

public class Metaspace

static javassist.ClassPool cp = javassist.ClassPool.getDefault();

public static void main(String args[]) throws Exception

for ( int i = 0 ; i < 100000 ; i++)

Class c = cp.makeClass( "com.saket.demo.Metaspace" + i).toClass();

Этот код будет генерировать новые классы и загружать их определения в Metaspace до тех пор, пока пространство не будет полностью использовано и не будет выброшено java.lang.OutOfMemoryError: Metaspace. Когда запущено с -XX: MaxMetaspaceSize = 64m, то в Mac OS X моя Java 1.8.0_05 умирает при загрузке около 70 000 классов.

Предупреждение: Если MaxMetaSpaceSize , был установлен в командной строке, увеличьте его значение. MetaSpace выделяется из тех же адресных пространств, что и куча Java. Уменьшение размера кучи Java сделает больше места доступным для MetaSpace. Это только правильный компромисс, если в куче Java имеется избыток свободного места.

// Java-программа для иллюстрации
// запрашиваемый размер массива
// превышает ошибку лимита виртуальной машины

public class GFG

static List<String> list = new ArrayList<String>();

public static void main(String args[]) throws Exception

Integer[] array = new Integer[ 10000 * 10000 ];

Java.lang.OutOfMemoryError: Запрашиваемый размер массива превышает ограничение виртуальной машины может появиться в результате одной из следующих ситуаций:

  • Ваши массивы становятся слишком большими и имеют размер между лимитом платформы и Integer.MAX_INT
  • Вы намеренно пытаетесь выделить массивы, превышающие 2 ^ 31-1 элементов, чтобы поэкспериментировать с ограничениями.
  • Операционная система настроена с недостаточно места подкачки.
  • Другой процесс в системе потребляет все ресурсы памяти.

// Java-программа для иллюстрации
// ошибка нового собственного потока

public class GFG

public static void main(String args[]) throws Exception

new Thread( new Runnable()

public void run()

catch (InterruptedException e)

Точное ограничение собственного потока зависит от платформы, например, тесты Mac OS X показывают, что:

Предотвращение: Используйте собственные утилиты ОС для дальнейшей диагностики проблемы. Для получения дополнительной информации об инструментах, доступных для различных операционных систем, см. Инструменты Native Operating System .

Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Читайте также: