Какие метрики смотреть при проблемах с памятью и что они означают
Что проверяют
Умеете ли вы по метрикам отличить утечку от нехватки памяти и от проблем со сборщиком, и знаете ли, что память JVM не равна куче. Следом обычно дают сценарий: «под растёт до лимита, его убивает OOM killer, а heap в норме. Что смотреть?»
Как отвечать
Идите по областям памяти и по тому, что говорит каждая метрика:
- heap после сборки мусора: важно не текущее заполнение, а уровень old gen после сборки. Пила, у которой нижние точки ползут вверх, это признак утечки;
- паузы и частота GC: доля времени в сборке и максимальная пауза. Частые сборки при почти полной куче значат, что приложению тесно или идёт утечка;
- allocation rate: сколько мегабайт в секунду выделяется. Высокий темп при нормальном heap говорит о лишнем мусоре, это бьёт по паузам и CPU;
- Metaspace: метаданные классов вне кучи. Растёт при утечке классов, например при генерации прокси или повторных редеплоях. PermGen убрали ещё в Java 8;
- off-heap: direct buffers (Netty, NIO), стеки потоков (около 1 МБ на поток на 64-битном Linux, задаётся
-Xss), code cache. Смотрится через-XX:NativeMemoryTracking=summaryиjcmd <pid> VM.native_memory; - RSS процесса: сколько процесс реально занимает у ОС. Именно его Kubernetes сравнивает с лимитом контейнера.
Инструменты: Micrometer с jvm.memory.used и jvm.gc.pause в Prometheus, GC-логи через -Xlog:gc*, Java Flight Recorder для профиля аллокаций, дамп кучи через jcmd <pid> GC.heap_dump или -XX:+HeapDumpOnOutOfMemoryError и разбор в Eclipse MAT.
Где спотыкаются
Бьют тревогу по высокому heap usage. Куча, заполненная на 90% перед сборкой, это нормальная работа G1, смотреть надо на уровень после неё.
Ставят -Xmx равным лимиту контейнера. Metaspace, стеки и direct buffers в него не входят, и процесс убивают без всякого OutOfMemoryError. В контейнере удобнее -XX:MaxRAMPercentage с запасом.
Не различают виды OutOfMemoryError: Java heap space, Metaspace, unable to create native thread и GC overhead limit exceeded лечатся совершенно по-разному.