🧠 Раздел 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/152-ФЗ).

Связанные темы