Оптимизация JVM: от медленного приложения до боевого продакшена за 30 минут

В этом подробном гайде разберём, как правильно настроить JVM, выбрать сборщик мусора, профилировать приложение и подготовить его к реальным нагрузкам. Практические примеры, готовые конфигурации и чеклист для продакшена — всё, что нужно начинающему разработчику.

ПрактикаРазработка

6 мин

Анатомия памяти JVM: понимаем Heap.

Heap (куча) — это область памяти, где хранятся все объекты вашего приложения. Она делится на несколько областей:

Структура Heap:

Young Generation (молодое поколение):

  • Eden Space — здесь создаются новые объекты

  • Survivor Space (S0 и S1) — объекты, пережившие первую сборку мусора

Old Generation (старое поколение):

  • Долгоживущие объекты, пережившие несколько циклов сборки мусора

Metaspace (заменил PermGen в Java 8+):

  • Метаданные классов, константы

Как настроить размер Heap?

# Установить минимальный и максимальный размер heap
java -Xms2g -Xmx4g -jar myapp.jar

# -Xms — начальный размер heap (2 GB)
# -Xmx — максимальный размер heap (4 GB)

Важное правило: Xms и Xmx лучше делать одинаковыми в продакшене, чтобы избежать изменения размера heap во время работы.

Как рассчитать нужный размер?

Для начала можно использовать формулу:

Heap Size = (Peak Live Data Size) × (2-4)

Где Peak Live Data Size — максимальный объём "живых" объектов после Full GC.

Garbage Collection: выбираем и настраиваем

GC (Garbage Collector) автоматически удаляет неиспользуемые объекты из памяти. Существует несколько типов сборщиков мусора, каждый со своими особенностями.

Основные типы GC:

1. Serial GC (для небольших приложений)

java -XX:+UseSerialGC -jar myapp.jar

Когда использовать: Однопоточные приложения, heap < 100 MB

2. Parallel GC (дефолтный в Java 8)

java -XX:+UseParallelGC -jar myapp.jar

Когда использовать: Приложения, где важна пропускная способность (throughput), можно терпеть паузы

3. G1GC (рекомендуется для большинства случаев)

java -XX:+UseG1GC -jar myapp.jar

Когда использовать: Heap > 4 GB, нужны предсказуемые паузы

Настройка целевого времени паузы:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar

4. ZGC и Shenandoah (для низколатентных приложений)

# ZGC (Java 11+, production-ready с Java 15)
java -XX:+UseZGC -jar myapp.jar

# Shenandoah
java -XX:+UseShenandoahGC -jar myapp.jar

Когда использовать: Критичны минимальные паузы (< 10 ms), большой heap

Мониторинг GC в логах

Включите подробные логи GC:

java -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m \
     -XX:+UseG1GC \
     -jar myapp.jar

Профилирование: находим узкие места

Профилирование помогает понять, где ваше приложение тратит время и память.

1. Используем JVisualVM (бесплатно)

JVisualVM входит в JDK и позволяет:

  • Мониторить использование CPU и памяти в реальном времени

  • Снимать heap dumps

  • Анализировать потоки

# Запустите приложение с JMX
java -Dcom.sun.management.jmxremote \
     -Dcom.sun.management.jmxremote.port=9010 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \
     -jar myapp.jar

Затем подключитесь через VisualVM к localhost:9010.

2. Анализ Heap Dump

Когда происходит OutOfMemoryError, полезно получить снимок памяти:

# Автоматический dump при OOM
java -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/tmp/heapdump.hprof \
     -jar myapp.jar

# Или создайте вручную
jmap -dump:live,format=b,file=heap.bin <PID>

Анализируйте dump в Eclipse MAT или VisualVM, ищите:

  • Объекты, занимающие больше всего памяти

  • Утечки памяти (memory leaks)

  • Неожиданно большие коллекции

3. Async Profiler (production-ready)

Для продакшена подходит async-profiler — не замедляет приложение:

# Скачать
wget https://github.com/jvm-profiling-tools/async-profiler/releases/latest/download/async-profiler-2.9-linux-x64.tar.gz

# Запустить профилирование на 60 секунд
./profiler.sh -d 60 -f flamegraph.html <PID>

Результат — flamegraph, где видно "горячие" методы.

4. Мониторинг метрик

Используйте Micrometer + Prometheus + Grafana:

// Spring Boot автоматически экспортирует JVM метрики
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Добавьте в application.properties:

management.endpoints.web.exposure.include=prometheus,health,metrics
management.metrics.export.prometheus.enabled=true

Типичные проблемы и их решения

Проблема 1: Частые Full GC

Симптомы: Приложение "замирает" на секунды

Решение:

# Увеличьте heap или настройте young generation
java -Xms4g -Xmx4g \
     -XX:NewRatio=2 \
     -XX:+UseG1GC \
     -jar myapp.jar

Проблема 2: OutOfMemoryError: Java heap space

Решение:

  1. Увеличьте -Xmx

  2. Найдите утечки памяти через heap dump

  3. Оптимизируйте код (избавьтесь от лишних коллекций)

Проблема 3: OutOfMemoryError: Metaspace

Решение:

# Увеличьте metaspace
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar myapp.jar

Проблема 4: Высокая загрузка CPU из-за GC

Решение:

  • Переключитесь на более эффективный GC (G1, ZGC)

  • Увеличьте heap

  • Оптимизируйте создание объектов в коде

Чеклист подготовки к продакшену

1. Настройки памяти

java -Xms4g -Xmx4g \                    # Фиксированный heap
     -XX:MetaspaceSize=256m \           # Metaspace
     -XX:MaxMetaspaceSize=512m \
     -XX:+UseG1GC \                     # G1 GC
     -XX:MaxGCPauseMillis=200 \         # Целевое время паузы
     -XX:+HeapDumpOnOutOfMemoryError \  # Dump при OOM
     -XX:HeapDumpPath=/var/log/app \
     -jar myapp.jar

2. Логирование GC

-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m

3. JMX для мониторинга

-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.ssl=true \
-Dcom.sun.management.jmxremote.password.file=/etc/jmx.password

4. Контейнеризация (Docker)

JVM автоматически определяет лимиты контейнера с Java 10+:

FROM openjdk:17-slim

# JVM автоматически увидит лимиты
ENV JAVA_OPTS="-XX:+UseContainerSupport \
               -XX:MaxRAMPercentage=75.0 \
               -XX:+UseG1GC"

CMD java $JAVA_OPTS -jar app.jar
# docker-compose.yml
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          memory: 2G

5. Мониторинг в продакшене

Обязательно отслеживайте:

  • Heap usage (используемая память)

  • GC frequency (частота сборок)

  • GC pause time (длительность пауз)

  • Thread count (количество потоков)

  • CPU usage

Пример готовой конфигурации

Для типичного Spring Boot приложения:

#!/bin/bash
java -Xms2g -Xmx2g \
     -XX:MetaspaceSize=256m \
     -XX:MaxMetaspaceSize=256m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
     -Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime:filecount=10,filesize=100m \
     -Dcom.sun.management.jmxremote \
     -Dcom.sun.management.jmxremote.port=9010 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \
     -jar myapp.jar

Дополнительные инструменты

  • GCEasy (gceasy.io) — анализ GC логов онлайн

  • FastThread (fastthread.io) — анализ thread dumps

  • JProfiler — коммерческий профайлер

  • YourKit — ещё один отличный коммерческий профайлер

  • Arthas — инструмент от Alibaba для диагностики в runtime

Выводы

Оптимизация JVM — это итеративный процесс. Начните с понимания базовых концепций (heap, GC, профилирование), используйте правильные инструменты для диагностики и постепенно оптимизируйте под вашу конкретную нагрузку.

Помните: преждевременная оптимизация — корень всех зол. Сначала измеряйте, потом оптимизируйте!

Было полезно?

Присоединяйтесь к Кодику — образовательной платформе для разработчиков!

У нас вы найдёте структурированные курсы, практические задания и актуальные материалы по Java, оптимизации производительности и подготовке к продакшену.

А ещё у нас есть крутой телеграм-канал с дружеским комьюнити, где опытные разработчики делятся знаниями, помогают решать проблемы и обсуждают лучшие практики. Присоединяйтесь!

Комментарии