Перейти к основному содержимому

Различные способы захвата дампов кучи Java

· 6 мин. чтения

1. Введение

В этом руководстве мы рассмотрим различные способы захвата дампа кучи в Java.

Дамп кучи — это снимок всех объектов, находящихся в памяти JVM в определенный момент . Они очень полезны для устранения проблем с утечкой памяти и оптимизации использования памяти в приложениях Java.

Дампы кучи обычно хранятся в файлах hprof двоичного формата. Мы можем открывать и анализировать эти файлы с помощью таких инструментов, как jhat или JVisualVM. Кроме того, пользователи Eclipse очень часто используют MAT .

В следующих разделах мы рассмотрим несколько инструментов и подходов к созданию дампа кучи и покажем основные различия между ними.

2. Инструменты JDK

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

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

2.1. jmap

jmap — это инструмент для вывода статистики о памяти в работающей JVM. Мы можем использовать его для локальных или удаленных процессов.

Чтобы захватить дамп кучи с помощью jmap, нам нужно использовать параметр дампа :

jmap -dump:[live],format=b,file=<file-path> <pid>

Наряду с этой опцией мы должны указать несколько параметров:

  • live : если установлено, печатаются только объекты, имеющие активные ссылки, и отбрасываются те, которые готовы к сборке мусора. Этот параметр является необязательным.
  • format=b : указывает, что файл дампа будет в двоичном формате. Если не установлено, результат тот же.
  • file : файл, в который будет записан дамп
  • pid : идентификатор процесса Java

Пример будет выглядеть так:

jmap -dump:live,format=b,file=/tmp/dump.hprof 12587

Помните, что мы можем легко получить pid процесса Java с помощью команды jps .

Кроме того, имейте в виду, что jmap был представлен в JDK как экспериментальный инструмент и не поддерживается. Поэтому в некоторых случаях может быть предпочтительнее использовать другие инструменты.

2.2. jcmd

jcmd — очень полный инструмент, который работает, отправляя командные запросы в JVM. Мы должны использовать его на той же машине, где запущен процесс Java.

Одна из его многочисленных команд — GC.heap_dump . Мы можем использовать его для получения дампа кучи, просто указав pid процесса и путь к выходному файлу:

jcmd <pid> GC.heap_dump <file-path>

Мы можем выполнить его с теми же параметрами, что и раньше:

jcmd 12587 GC.heap_dump /tmp/dump.hprof

Как и в случае с jmap, созданный дамп имеет двоичный формат.

2.3. JVisualVM

JVisualVM — это инструмент с графическим пользовательским интерфейсом, который позволяет нам отслеживать, устранять неполадки и профилировать приложения Java . Графический интерфейс простой, но очень интуитивно понятный и простой в использовании.

Один из его многочисленных вариантов позволяет нам захватить дамп кучи. Если мы щелкнем правой кнопкой мыши процесс Java и выберем опцию «Дамп кучи» , инструмент создаст дамп кучи и откроет его на новой вкладке:

./7b944a9466d2376d30272b2550a8faaa.png

Обратите внимание, что мы можем найти путь к файлу, созданному в разделе «Основная информация» .

Начиная с JDK 9, Visual VM не входит в дистрибутивы Oracle JDK и Open JDK. Поэтому, если мы используем что-то более новое, чем Java 9, мы можем получить JVisualVM с сайта проекта Visual VM с открытым исходным кодом .

3. Автоматический захват дампа кучи

Все инструменты, которые мы показали в предыдущих разделах, предназначены для захвата дампов кучи вручную в определенное время. В некоторых случаях мы хотим получить дамп кучи, когда возникает java.lang.OutOfMemoryError , чтобы помочь нам исследовать ошибку.

Для этих случаев Java предоставляет параметр командной строки HeapDumpOnOutOfMemoryError , который создает дамп кучи при возникновении ошибки java.lang.OutOfMemoryError :

java -XX:+HeapDumpOnOutOfMemoryError

По умолчанию он сохраняет дамп в файле java_pid<pid>.hprof в каталоге, где мы запускаем приложение. Если мы хотим указать другой файл или каталог, мы можем установить его в опции HeapDumpPath :

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>

Когда нашему приложению не хватит памяти с помощью этой опции, мы сможем найти созданный файл, содержащий дамп кучи, в логах:

java.lang.OutOfMemoryError: Requested array size exceeds VM limit
Dumping heap to java_pid12587.hprof ...
Exception in thread "main" Heap dump file created [4744371 bytes in 0.029 secs]
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at com.foreach.heapdump.App.main(App.java:7)

В приведенном выше примере он был записан в файл java_pid12587.hprof .

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

Наконец, этот параметр также можно указать во время выполнения с помощью HotSpotDiagnostic MBean . Для этого мы можем использовать JConsole и установить для параметра виртуальной машины HeapDumpOnOutOfMemoryError значение true :

./c68c0470a4be8da49f2f78a4e800e70b.png

Дополнительную информацию о MBeans и JMX мы можем найти в этой статье .

4. ДжМХ

Последний подход, который мы рассмотрим в этой статье, — это использование JMX. Мы будем использовать HotSpotDiagnostic MBean , кратко представленный в предыдущем разделе. Этот MBean предоставляет метод dumpHeap , который принимает два параметра:

  • outputFile : путь к файлу дампа. Этот файл должен иметь расширение hprof.
  • live : если установлено значение true, он выгружает только активные объекты в памяти, как мы видели ранее с jmap.

В следующих разделах мы покажем два разных способа вызова этого метода для захвата дампа кучи.

4.1. JConsole

Самый простой способ использовать HotSpotDiagnostic MBean — использовать клиент JMX, например JConsole.

Если мы откроем JConsole и подключимся к запущенному процессу Java, мы сможем перейти на вкладку MBeans и найти HotSpotDiagnostic в разделе com.sun.management . В операциях мы можем найти описанный ранее метод dumpHeap :

./29c05b269be2a3a6bbf6ef5b34d27212.png

Как показано, нам просто нужно ввести параметры outputFile и live в текстовые поля p0 и p1 , чтобы выполнить операцию dumpHeap .

4.2. Программный способ

Другой способ использования MBean HotSpotDiagnostic — программный вызов его из кода Java.

Для этого нам сначала нужно получить экземпляр MBeanServer , чтобы получить MBean, зарегистрированный в приложении. После этого нам просто нужно получить экземпляр HotSpotDiagnosticMXBean, и вызвать его метод dumpHeap .

Давайте посмотрим на это в коде:

public static void dumpHeap(String filePath, boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
}

Обратите внимание, что файл hprof нельзя перезаписать. Поэтому мы должны учитывать это при создании приложения, которое печатает дампы кучи. Если мы этого не сделаем, мы получим исключение:

Exception in thread "main" java.io.IOException: File exists
at sun.management.HotSpotDiagnostic.dumpHeap0(Native Method)
at sun.management.HotSpotDiagnostic.dumpHeap(HotSpotDiagnostic.java:60)

5. Вывод

В этой статье мы узнали несколько способов захвата дампа кучи в Java.

Как правило, мы всегда должны помнить об использовании параметра HeapDumpOnOutOfMemoryError при запуске приложений Java. Для разных целей можно использовать любой из других инструментов, главное помнить о неподдерживаемом статусе jmap.

Как всегда, полный исходный код примеров доступен на GitHub .