🧠 Розділ 3 · Питання #23

Що таке heap dump

Якщо уявити Java-застосунок як квартиру, то Heap Dump — це детальна фотозйомка кожного предмета в кожній кімнаті: що де лежить, якого воно розміру та як пов'язане з іншими предм...


🟢 Junior Level

Heap Dump (дамп купи) — це моментальний знімок (snapshot) вмісту пам’яті Java Heap у двійковому форматі (зазвичай із розширенням .hprof), зафіксований у певний момент часу.

Проста аналогія

Якщо уявити Java-застосунок як квартиру, то Heap Dump — це детальна фотозйомка кожного предмета в кожній кімнаті: що де лежить, якого воно розміру та як пов’язане з іншими предметами.

Що містить Heap Dump:

  • Усі завантажені в JVM класи та їхні метаописи;
  • Усі створені в купі об’єкти (екземпляри класів) зі значеннями їхніх полів;
  • Усі масиви об’єктів та масиви примітивних типів (byte[], char[], int[]) з їхнім фактичним бінарним вмістом;
  • Точки входу для збирача сміття (GC Roots): статичні змінні класів, стеки активних потоків, JNI-посилання.

Чого НЕМАЄ в Heap Dump:

  • Нативної пам’яті C++ (Off-Heap, Direct Byte Buffers поза купою);
  • Машинного коду JIT-компілятора (Code Cache);
  • Детальної інформації про стан блокувань потоків та стек-трейсів виконання (це завдання Thread Dump).

🟡 Middle Level

1. Внутрішній формат HPROF

Файл дампу зберігається за стандартом двійкового формату HPROF (JAVA PROFILE 1.0.2):

  • Заголовок (Header): текстова сигнатура формату, розмір покажчика (4 байти для 32-біт/Compressed OOPs або 8 байтів для 64-біт) і мітка часу створення.
  • Потік записів (Records):
    • HPROF_GC_ROOT_* — покажчики на корені досяжності (потоки, стек, JNI, монітори);
    • HPROF_GC_CLASS_DUMP — опис класу (розмір, статичні поля, константи);
    • HPROF_GC_INSTANCE_DUMP — сирі значення полів екземпляра об’єкта;
    • HPROF_GC_PRIM_ARRAY_DUMP / OBJ_ARRAY_DUMP — неперервні масиви даних.

2. Зняття: Live vs All (Ілюзія швидкості)

При знятті дампу через консольну утиліту jcmd доступний параметр -all:

# Варіант 1: Зняти ВСІ об'єкти (живі + мертві)
jcmd <pid> GC.heap_dump -all=true /dumps/full_heap.hprof

# Варіант 2: Зняти ТІЛЬКИ ЖИВІ об'єкти
jcmd <pid> GC.heap_dump -all=false /dumps/live_heap.hprof

[!WARNING] Парадокс продуктивності:

  • Багато хто помилково вважає, що -all=false (лише живі об’єкти) знімається швидше, оскільки підсумковий файл менший.
  • В реальності: щоб відфільтрувати мертві об’єкти від живих, JVM зобов’язана примусово запустити синхронний Full GC безпосередньо перед серіалізацією!
  • У результаті -all=false накладає час тривалого Full GC Stop-the-World поверх часу запису на диск. Варіант -all=true не вимагає попереднього GC й одразу починає скидання пам’яті.

3. Автоматичне збереження при OOME

Головне виробниче джерело дампів — автоматична генерація віртуальною машиною при критичній відмові:

java -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/dumps/ \
     -XX:+ExitOnOutOfMemoryError \
     -jar app.jar
  • Дамп формується JVM у момент першої неможливості виділити пам’ять;
  • Дозволяє постфактум відтворити точну причину падіння на робочій машині розробника.

🔴 Senior Level

1. Архітектурні ризики Heap Dump у Kubernetes

Зняття дампу пам’яті в розподілених високонавантажених системах пов’язане з трьома критичними загрозами:

[JVM Process] ─── Safepoint STW (10-40 сек) ───► [Запис 32 ГБ на диск]
     │                                                     │
     ▼                                                     ▼
K8s Liveness Probe Fail!                        Диск переповнений (DiskPressure)
     │                                                     │
     ▼                                                     ▼
SIGKILL 137 (Под убитий, дамп пошкоджений)       Eviction усіх подів на ноді
  1. Тривалий Stop-The-World (TTSP + I/O): JVM зобов’язана зупинити всі потоки застосунку на час обходу та запису купи на диск. На дисках зі швидкістю запису 200 МБ/с запис купи розміром 32 ГБ триватиме від 15 до 30 секунд. За цей період Kubernetes Liveness Probe не отримає відповіді, вирішить, що под завис, і примусово завершить контейнер сигналом SIGKILL, зробивши файл дампу нечитабельним.
  2. Переповнення ефемерного сховища (Ephemeral Storage Limit): Якщо шлях -XX:HeapDumpPath не примонтований до окремого постійного тому (PersistentVolume), дамп запишеться в overlayfs контейнера. Це миттєво викличе перевищення лімітів диска та видалення пода з ноди (Pod Eviction).
  3. Безпечне рішення: Перед зняттям дампу вручну под необхідно відключити від балансувальника трафіку (traffic drain / kubectl cordon/drain), або використовувати фонове профілювання без STW (JFR OldObjectSample).

2. Стиснення на льоту: прапорець -XX:+HeapDumpGzip

Починаючи з Java 18 (і бекпортовано в оновлені дистрибутиви Java 11/17), JVM підтримує потокове стиснення дампу купи алгоритмом Gzip:

# Увімкнення стиснення дампу купи з рівнем компресії від 1 (швидко) до 9 (максимально)
java -XX:+HeapDumpOnOutOfMemoryError \
     -XX:+HeapDumpGzip \
     -XX:HeapDumpGzip=3 \
     -XX:HeapDumpPath=/dumps/app_heap.hprof.gz \
     -jar app.jar
  • Зменшує розмір файлу на диску на 60–80% (наприклад, з 32 ГБ до 6–8 ГБ);
  • Суттєво прискорює передачу файлів мережею та знижує навантаження на дискову підсистему вводу-виводу (I/O).

3. Інформаційна безпека: витік PII та секретів

[!CAUTION] Heap Dump — це відкрита копія всієї оперативної пам’яті застосунку. Він містить:

  • Паролі до баз даних та API-токени (JWT, секретні ключі) у відкритому вигляді всередині рядків String та масивів byte[]/char[];
  • Персональні дані користувачів (PII): номери банківських карток, паспортні дані, телефони, листування.

Правила безпеки:

  1. Файли .hprof категорично заборонено пересилати відкритими каналами (пошта, месенджери, тікети Jira).
  2. Доступ до дампів на серверах має бути суворо обмежений правами chmod 600.
  3. Для передачі дампу стороннім експертам обов’язково застосовувати утиліти знеособлення та маскування даних (наприклад, RedactHprof або вбудовані фільтри Eclipse MAT), які замінюють значення масивів символів нулями зі збереженням структури графу класів.

🎯 Шпаргалка для інтерв’ю

30-секундна відповідь для співбесіди

«Heap Dump — це бінарний знімок усієї купи JVM у форматі HPROF, що фіксує всі класи, об’єкти, їхні примітивні поля, масиви та корені досяжності (GC Roots). Дамп знімається або автоматично при аварії за прапорцем -XX:+HeapDumpOnOutOfMemoryError, або вручну через jcmd <pid> GC.heap_dump. Головний ризик зняття дампу на проді — глибокий Stop-The-World на десятки секунд для запису гігабайтів даних на диск, що може призвести до вбивства контейнера оркестратором. Важливо пам’ятати, що дамп містить усі секрети та персональні дані у відкритому вигляді, тому вимагає суворого контролю доступу».


Питання підвищеної складності з відповідями

  1. Чому зняття дампу з параметром -all=false (тільки живі об’єкти) може призвести до більшої затримки, ніж -all=true?
    • Відповідь: Параметр -all=false змушує JVM виконати повне збирання сміття (Full GC) перед записом файлу, щоб визначити точний граф досяжності та відсіяти неживі об’єкти. Хоча результуючий файл на диску виходить меншим, загальна пауза Stop-The-World складається з тривалості Full GC та часу запису файлу, що сумарно може виявитися значно довше за просте послідовне скидання всієї пам’яті без попереднього збирання (-all=true).
  2. Чи містить Heap Dump стек-трейси потоків та виконуваний байткод?
    • Відповідь: Ні. Heap Dump містить лише об’єкти в купі, їхні поля, масиви та посилання на класи і GC Roots. Детальні стек-трейси потоків, поточні рядки виконуваного коду, адреси PC (Program Counter) та списки очікуваних моніторів містяться в Thread Dump. Для повної картини збою рекомендується зберігати обидва артефакти.
  3. Що станеться, якщо в Kubernetes под упаде за OOME з прапорцем -XX:+HeapDumpOnOutOfMemoryError, але шлях збереження не примонтований до PersistentVolume?
    • Відповідь: Файл дампу запишеться в ефемерний шар контейнера (overlayfs). Якщо запис дампу призведе до вичерпання дискової квоти пода, нода K8s ініціює негайний Eviction. Після того як контейнер перезапуститься, його файлова система буде створена наново, а згенерований дамп пам’яті буде безповоротно втрачений. Тому шлях -XX:HeapDumpPath завжди повинен вказувати на змонтований мережевий або постійний диск.
  4. Як безпечно передати Heap Dump зовнішньому підряднику для аналізу без розкриття комерційної таємниці та персональних даних?
    • Відповідь: Використовувати утиліти знеособлення дампів пам’яті (наприклад, RedactHprof). Вони перезаписують значення всіх символьних і байтових масивів (включно з приватними полями рядків String.value), замінюючи реальні текстові дані, паролі та номери карток на псевдовипадкові послідовності або нулі. При цьому граф об’єктів, їхні типи, посилання та обсяги пам’яті залишаються незайманими, що дозволяє аналітику знайти витік, не бачачи реальних даних клієнтів.

Типові помилки та Red Flags

  • 🚩 Вважати, що Heap Dump містить вихідний код програми та трасування виконання потоків. (Red Flag: нерозуміння структури пам’яті та різниці між Heap Dump і Thread Dump).
  • 🚩 Думати, що дамп «лише живих об’єктів» не впливає на працюючий застосунок. (Red Flag: нерозуміння того, що фільтрація живих об’єктів вимагає обов’язкового виклику Full GC).
  • 🚩 Надсилати сирий .hprof файл бойової бази користувачів через незахищені канали зв’язку. (Грубе порушення вимог інформаційної безпеки та регуляцій GDPR).

Пов’язані теми