Как начать анализ периодических падений сервиса из-за OutOfMemoryError (OOME)?

«Как начать анализ периодических падений сервиса из-за OutOfMemoryError (OOME)?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Анализ следует начать со сбора и изучения дампа памяти (heap dump), созданного в момент ошибки OOME.

План действий:

  1. Включите автоматическое создание дампа при OOME, добавив в аргументы JVM:
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dumps
  2. Проанализируйте дамп с помощью профилировщиков:
    • Eclipse Memory Analyzer (MAT) — мощный инструмент для поиска утечек.
    • VisualVM или JProfiler — для интерактивного анализа. В них нужно:
    • Найти объекты, занимающие наибольший объем памяти (вкладка Histogram или Biggest Objects).
    • Построить путь до GC Root для этих объектов, чтобы понять, что удерживает их в памяти.
    • Искать паттерны утечек, например, коллекции, которые бесконечно растут.
  3. Изучите логи сборки мусора (GC logs), включив их:
    -Xlog:gc*:file=gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M

    Обратите внимание на частоту и длительность сборок мусора, особенно Full GC.

Пример кода с потенциальной утечкой (статическая коллекция):

public class LeakyCache {
    private static final Map<String, byte[]> CACHE = new HashMap<>();

    public void storeData(String id, byte[] data) {
        // Данные добавляются, но никогда не удаляются -> утечка
        CACHE.put(id, data);
    }
}

Дополнительные шаги: проверка настроек памяти (-Xmx, -Xms), мониторинг метрик (через JMX/Prometheus) и анализ кода на наличие подобных паттернов.