Як отримати heap dump
Найнадійніший і найпоширеніший підхід — налаштувати віртуальну машину так, щоб вона сама зберігала знімок пам'яті при вичерпанні ресурсів:
🟢 Junior Level
Heap Dump (дамп купи) можна отримати кількома способами: автоматично при збої застосунку, вручну через консоль або за допомогою графічних інструментів розробки.
1. Автоматично при OutOfMemoryError (Production Must-Have)
Найнадійніший і найпоширеніший підхід — налаштувати віртуальну машину так, щоб вона сама зберігала знімок пам’яті при вичерпанні ресурсів:
java -XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/dumps/app_oom.hprof \
-jar app.jar
2. Вручну через консольну утиліту jcmd
Якщо застосунок працює повільно або є підозра на витік, дамп можна зняти на льоту:
# 1. Дізнатися PID процесу
jps
# або: ps aux | grep java
# 2. Зняти дамп пам'яті
jcmd <PID> GC.heap_dump /tmp/heap_dump.hprof
3. Через графічний інтерфейс (VisualVM / IntelliJ IDEA)
- У VisualVM: знайти процес у списку ліворуч, натиснути правою кнопкою миші $\to$ Heap Dump.
- В IntelliJ IDEA Ultimate: відкрити вкладку Profiler $\to$ вибрати запущений процес $\to$ Capture Memory Snapshot.
🟡 Middle Level
1. Просунуті параметри зняття через jcmd
Утиліта jcmd підтримує параметри стиснення та фільтрації:
# Стиснення на льоту архіватором gzip (Java 11u12+ та Java 17+)
# -gz=1 (мінімальне стиснення, мінімальне навантаження на CPU)
# -gz=9 (максимальне стиснення, менший розмір файлу)
jcmd <PID> GC.heap_dump -gz=1 /tmp/dump.hprof.gz
# Зняття ТІЛЬКИ ЖИВИХ об'єктів (ініціює попередній Full GC)
jcmd <PID> GC.heap_dump -all=false /tmp/live_dump.hprof
[!NOTE] Файл дампу купи розміром 32 ГБ при прапорці
-gz=1стискається до 6–8 ГБ. Це економить місце на диску і в рази скорочує час завантаження дампу з віддаленого сервера.
2. Програмне зняття з Java-коду
HotSpot надає офіційний JMX-бін HotSpotDiagnosticMXBean, що дозволяє запросити дамп безпосередньо з бізнес-коду:
import com.sun.management.HotSpotDiagnosticMXBean;
import java.io.IOException;
import java.lang.management.ManagementFactory;
public class DiagnosticService {
public static void dumpHeap(String filePath, boolean liveOnly) throws IOException {
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class
);
// liveOnly=true викликає попередній Full GC
mxBean.dumpHeap(filePath, liveOnly);
}
}
3. Зняття через Spring Boot Actuator
У мікросервісах на Spring Boot доступний вбудований ендпоінт Actuator:
# Завантаження дампу купи через HTTP GET
curl -O http://localhost:8080/actuator/heapdump
- Під капотом Actuator звертається до
HotSpotDiagnosticMXBean, зберігає тимчасовий файл і передає його у вигляді HTTP-відповіді (application/octet-stream). - ⚠️ Ризик безпеки: цей ендпоінт зобов’язаний бути захищений авторизацією (роль ADMIN) або закритий від зовнішньої мережі, інакше зловмисник зможе завантажити всю пам’ять сервісу з токенами та паролями.
🔴 Senior Level
1. Механіка Attach API та пастки прав доступу в контейнерах
Команда jcmd взаємодіє з цільовою віртуальною машиною через HotSpot Attach API:
- Клієнт
jcmdстворює сигнальний файл у каталозі тимчасових файлів (/tmp/.java_pid<PID>); - Відправляє процесу JVM сигнал
SIGQUIT(3); - Потік
Signal Dispatcherвсередині JVM активує потікAttach Listener, який відкриває Unix Domain Socket (/tmp/.java_pid<PID>); jcmdпередає командуGC.heap_dumpчерез сокет.
Типові збої в Docker / Kubernetes:
- Помилка
Unable to open socket file: У production контейнери запускаються під непривілейованим користувачем (наприклад,appuserз UID 10001). Якщо увійти в контейнер підrootі виконатиjcmd 1 GC.heap_dump ..., команда завершиться з помилкою прав доступу. Рішення: виконувати команду від імені того ж користувача, під яким працює процес:su -s /bin/sh appuser -c "jcmd 1 GC.heap_dump -gz=1 /tmp/dump.hprof.gz" - Ізоляція каталогу
/tmp: Якщо/tmpзмонтовано з опцієюnoexecабо JVM запущено з прапорцем-XX:+DisableAttachMechanism,jcmdне зможе підключитися.
2. Зняття дампу з пода Kubernetes під бойовим навантаженням
Покроковий сценарій вивантаження пам’яті без падіння сервісу:
# Крок 1: Ізолювати под від вхідного трафіку (видалити лейбл селектора сервісу)
kubectl label pod order-service-7f8d-xkz pod-under-diag=true --overwrite
kubectl label pod order-service-7f8d-xkz app-
# Крок 2: Виконати команду зняття дампу зі стисненням на змонтований том
kubectl exec order-service-7f8d-xkz -- jcmd 1 GC.heap_dump -gz=1 /dumps/diag.hprof.gz
# Крок 3: Скопіювати дамп на локальну станцію інженера
kubectl cp order-service-7f8d-xkz:/dumps/diag.hprof.gz ./diag.hprof.gz
# Крок 4: Видалити тимчасовий файл дампу з пода
kubectl exec order-service-7f8d-xkz -- rm /dumps/diag.hprof.gz
# Крок 5: Перезапустити або видалити под, щоб відновити балансування
kubectl delete pod order-service-7f8d-xkz
3. Зняття дампу через GNU Debugger (gcore)
Якщо JVM повністю зависла в мертвому циклі (Thread deadlock, uninterruptible sleep, пошкодження структур даних) і не відповідає на команди Attach API (jcmd висить нескінченно), єдиним способом зняти пам’ять процесу є генерація системного core dump через ядро Linux:
# Зняття системного дампу пам'яті ядра Linux без участі JVM
gcore -o /dumps/core.dump <PID>
# Конвертація системного core dump у Java HPROF за допомогою утиліти jhsdb
jhsdb jmap --exe $JAVA_HOME/bin/java --core /dumps/core.dump.<PID> --binaryheap --dumpfile /dumps/converted.hprof
Цей підхід працює на рівні ядра ОС і дозволяє досліджувати навіть намертво завислі або впалі процеси.
🎯 Шпаргалка для інтерв’ю
30-секундна відповідь для співбесіди
«Отримати Heap Dump можна автоматично при збої через параметри віртуальної машини
-XX:+HeapDumpOnOutOfMemoryErrorта-XX:HeapDumpPath, або вручну через консольну утилітуjcmd <PID> GC.heap_dump [-gz=1] <path>. У сучасних версіях Java рекомендується використовувати прапорець-gz=1для стиснення дампу на льоту, що заощаджує до 75% місця. У коді дамп можна зняти черезHotSpotDiagnosticMXBeanабо ендпоінт Spring Boot Actuator/actuator/heapdump. У контейнерах важливо пам’ятати про права користувача (UID):jcmdповинен запускатися з-під того ж користувача, що й процес Java».
Питання підвищеної складності з відповідями
- Чому
jcmd <PID> GC.heap_dumpусередині Docker-контейнера може видати помилкуUnable to open socket file?- Відповідь: Attach API JVM спирається на створення Unix domain socket у каталозі
/tmp/.java_pid<PID>. Якщо оператор заходить у контейнер черезkubectl execпід користувачемroot, а Java-процес запущено під непривілейованим користувачем (наприклад,appuser), ядро ОС заборонить доступ до сокета через незбіг прав UID/GID. Для вирішення командиjcmdнеобхідно виконувати від імені того самого користувача, під яким працює JVM (su -s /bin/sh appuser -c "jcmd ...").
- Відповідь: Attach API JVM спирається на створення Unix domain socket у каталозі
- Чим небезпечний виклик
/actuator/heapdumpна бойовому Spring Boot сервісі?- Відповідь: Ендпоінт запускає синхронне зняття дампу пам’яті через
HotSpotDiagnosticMXBean. Це:- Вводить JVM у Stop-The-World паузу на весь час формування дампу;
- Забиває мережу та процесор передачею десятків гігабайтів потоку даних;
- Створює ризик відмови в обслуговуванні (DoS);
- Несе критичну загрозу безпеці: дамп містить відкриті токени, ключі шифрування та персональні дані користувачів.
- Відповідь: Ендпоінт запускає синхронне зняття дампу пам’яті через
- Що станеться, якщо під час виконання
GC.heap_dumpна сервері закінчиться вільне місце на диску?- Відповідь: Системний виклик запису на диск поверне помилку
No space left on device(ENOSPC). JVM перехопить цю помилку вводу-виводу, перерве операцію збереження дампу, вийде з точки зупинки Safepoint і продовжить роботу застосунку. При цьому на диску залишиться незавершений пошкоджений.hprofфайл, який не зможе бути відкритий стандартними аналізаторами без попереднього ручного відновлення.
- Відповідь: Системний виклик запису на диск поверне помилку
- У чому різниця між зняттям дампу через
jcmdта зняттям черезgcore + jhsdb?- Відповідь:
jcmdпрацює через механізм Attach API самої віртуальної машини HotSpot і вимагає, щоб JVM була живою і здатною зупинити потоки в точці Safepoint.gcore— це утиліта ОС Linux, що знімає повний дамп адресного простору процесу через ядро операційної системи незалежно від стану JVM.gcoreзастосовується в аварійних ситуаціях, коли JVM наглухо зависла на нативному рівні або в непереривному системному виклику іjcmdне відповідає.
- Відповідь:
Типові помилки та Red Flags
- 🚩 Рекомендувати застарілу утиліту
jmapзамістьjcmd. (Red Flag: застарілі знання;jcmdє рекомендованим стандартом HotSpot). - 🚩 Залишати незахищеним ендпоінт
/actuator/heapdumpу публічному контурі. (Критична вразливість безпеки: витік облікових записів та даних користувачів). - 🚩 Знімати дамп у контейнері в кореневу файлову систему (
/tmp), не перевіривши дискові квоти. (Ризик покласти всю ноду Kubernetes через DiskPressure).