Как отлаживать исключения Java OutOfMemory?

Каков наилучший способ отладки исключений java.lang.OutOfMemoryError?

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


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

Ответ 1

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

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

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

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

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

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

Позвольте приложению работать, пока OutOfMemoryError произойдет OutOfMemoryError.

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

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

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

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

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

IBM HeapAnalyzer

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

java -Xmx10g -jar ha456.jar

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

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

jmap -histo:live <pid> | head -n <top N things to look for> > <output.log>

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

Для получения более подробной информации см. следующую ссылку http://www.lshift.net/blog/2006/03/08/java-memory-profiling-with-jmap-and-jhat

Вот еще одна ссылка на закладку http://java.sun.com/developer/technicalArticles/J2SE/monitoring/

Ответ 5

Каков наилучший способ отладки исключений java.lang.OutOfMemoryError?

OutOfMemoryError описывает тип ошибки в описании сообщения. Вы должны проверить описание сообщения об ошибке для обработки исключения.

Существуют различные причины возникновения исключений из памяти. Подробнее см. Документацию oracle страница.

java.lang.OutOfMemoryError: Java heap space:

Причина: подробное сообщение. Явное пространство Java указывает, что объект не может быть выделен в куче Java.

java.lang.OutOfMemoryError: GC Overhead limit exceeded:

Причина: Подробное сообщение "Превышение предельных значений GC" указывает, что сборщик мусора работает все время, а программа Java делает очень медленный прогресс.

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

Причина: Детальное сообщение "Требуемый размер массива превышает лимит VM" указывает, что приложение (или API-интерфейсы, используемые этим приложением) пыталось выделить массив, размер которого больше размера кучи.

java.lang.OutOfMemoryError: Metaspace:

Причина: метаданные класса Java (внутренняя презентация виртуальных машин класса Java) выделяются в собственной памяти (здесь называется metaspace)

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

Причина: подробное сообщение "байты размера запроса по причине. Из пространства подкачки?" представляется исключением OutOfMemoryError. Тем не менее, код Java HotSpot VM сообщает об этом кажущемся исключении, когда выделение из нативной кучи не удалось, а нативная куча могла быть близка к исчерпанию

java.lang.OutOfMemoryError: Compressed class space

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

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

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

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

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

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

Разница между javacore, дампом потока и дампом кучи в Websphere

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

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

Ответ 7

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

Это должно указывать вам в правильном направлении. Затем используйте стандартные методы отладки (ведение журнала, отладчик и т.д.), Чтобы отточить проблему. Используйте класс Runtime, чтобы получить текущее использование памяти и зарегистрировать его, когда выполняется соответствующий метод или процесс.

Ответ 8

Обычно я использую Eclipse Memory Analyzer. В нем отображаются подозреваемые преступники (объекты, которые занимают большую часть кучи кучи), и разные иерархии вызовов, которые генерируют эти объекты. Как только это сопоставление есть, мы можем вернуться к коду и попытаться понять, есть ли какая-либо возможная утечка памяти в любом месте в пути кода.

Однако OOM не всегда означает, что происходит утечка памяти. Всегда возможно, что память, необходимая приложению в стабильном состоянии или под нагрузкой, недоступна в аппаратной/виртуальной машине. Например, может существовать 32-битный Java-процесс (максимальная память используется ~ 4 ГБ), где у VM всего 3 ГБ. В таком случае изначально приложение может работать нормально, но OOM может возникать, когда и когда потребность в памяти приближается к 3 ГБ.

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