Оптимизация JVM: от медленного приложения до боевого продакшена за 30 минут
В этом подробном гайде разберём, как правильно настроить JVM, выбрать сборщик мусора, профилировать приложение и подготовить его к реальным нагрузкам. Практические примеры, готовые конфигурации и чеклист для продакшена — всё, что нужно начинающему разработчику.
Анатомия памяти 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.jar4. 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
Решение:
Увеличьте
-XmxНайдите утечки памяти через heap dump
Оптимизируйте код (избавьтесь от лишних коллекций)
Проблема 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.jar2. Логирование GC
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m3. 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.password4. Контейнеризация (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: 2G5. Мониторинг в продакшене
Обязательно отслеживайте:
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, оптимизации производительности и подготовке к продакшену.
А ещё у нас есть крутой телеграм-канал с дружеским комьюнити, где опытные разработчики делятся знаниями, помогают решать проблемы и обсуждают лучшие практики. Присоединяйтесь!