Расскажите про опыт тюнинга памяти и использования heap dump
Что проверяют
Разбирали ли вы реальный инцидент с памятью или знаете тему по статьям. Хороший ответ это последовательность действий, а не список инструментов.
Как отвечать
Расскажите по шагам, как ищется проблема.
- симптом и метрики: растёт время пауз, приложение перезапускается по OOM, память после сборки мусора не возвращается к прежнему уровню. Смотрятся логи сборщика (
-Xlog:gc*), метрики JVM в Prometheus,jcmd <pid> GC.heap_info; - снятие дампа:
jcmd <pid> GC.heap_dump /tmp/heap.hprof(устаревший вариант этоjmap), а на боевом стенде обязательно-XX:+HeapDumpOnOutOfMemoryErrorс указанием каталога: дамп в момент падения ценнее любых догадок; - анализ: Eclipse MAT, дерево доминаторов и отчёт Leak Suspects, VisualVM или JFR как альтернатива. Ищем, кто удерживает ссылки: статические коллекции, кеш без ограничения размера,
ThreadLocalв пуле потоков, неотписанные слушатели, накопленные соединения; - настройка:
-Xmsи-Xmxставят одинаковыми на сервере, чтобы куча не расширялась в ходе работы, выбирается сборщик (G1 по умолчанию, ZGC там, где важны паузы), задаётся цель по паузам. В контейнере вместо абсолютных значений удобнее-XX:MaxRAMPercentage; - проверка: тот же сценарий нагрузки до и после, сравнение по метрикам, а не по ощущениям.
Полезно назвать разницу между «памяти не хватает» и «утечка»: в первом случае кучу увеличивают, во втором увеличение просто отодвигает падение.
Где спотыкаются
Снимают дамп на проде под нагрузкой и не предупреждают команду: операция останавливает приложение на время записи файла.
Считают, что вся память процесса это куча. Метапространство, стеки потоков, буферы вне кучи и нативные библиотеки тоже занимают память, и OOM бывает не в куче.
Крутят флаги JVM наугад. Сначала измерение, потом настройка, иначе результат не отличить от совпадения.