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 и выберем опцию «Дамп кучи»
, инструмент создаст дамп кучи и откроет его на новой вкладке:
Обратите внимание, что мы можем найти путь к файлу, созданному в разделе «Основная информация»
.
Начиная с 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
:
Дополнительную информацию о 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
:
Как показано, нам просто нужно ввести параметры 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 .