JavaRush /Курсы /JAVA 25 SELF /Сборщики мусора: G1, ZGC, Shenandoah, сравнение

Сборщики мусора: G1, ZGC, Shenandoah, сравнение

JAVA 25 SELF
64 уровень , 1 лекция
Открыта

1. Знакомство со сборщиком мусора (GC)

Если вы когда-нибудь писали на C или C++, то наверняка сталкивались с необходимостью вручную освобождать память с помощью free() или delete. В Java всё гораздо проще: вы создаёте объект через new, а удалять его не нужно — этим занимается специальный «дворник» под названием сборщик мусора (Garbage Collector, GC).

GC — это часть JVM, которая автоматически освобождает память, занятую объектами, на которые больше нет ссылок. Благодаря ему Java‑разработчики могут не переживать, что забудут очистить память (и получат утечку), или, наоборот, случайно удалят объект, который всё ещё нужен (и словят краш).

Но, как и любой дворник, GC не идеален: порой он может вмешаться в самый неподходящий момент, устроив «генеральную уборку» (Stop-the-World), или работать не так быстро, как хотелось бы. Поэтому в JVM существует несколько разных реализаций сборщика мусора — и выбор правильной может заметно повлиять на производительность приложения.

Основные типы сборщиков мусора

Serial GC

  • Serial GC — самый простой и старый сборщик мусора.
  • Работает в одном потоке.
  • Останавливает все остальные потоки на время уборки (Stop-the-World).
  • Хорош для маленьких приложений без активной многопоточности.
  • Включается флагом: -XX:+UseSerialGC

Parallel GC

  • Parallel GC (также «Throughput Collector»).
  • Использует несколько потоков для уборки мусора.
  • Ориентирован на максимальную пропускную способность.
  • По‑прежнему выполняет уборку с паузами Stop-the-World, но быстрее, чем Serial.
  • Подходит для серверных приложений, где небольшие паузы некритичны.
  • Включается флагом: -XX:+UseParallelGC

CMS (Concurrent Mark Sweep)

  • CMS — устаревший, но долго популярный GC, минимизирующий паузы.
  • Работает частично параллельно с приложением, уменьшая время остановки.
  • Более сложен в настройке, имеет накладные расходы.
  • С Java 9 помечен как устаревший (deprecated).
  • Включается флагом: -XX:+UseConcMarkSweepGC

G1 (Garbage First)

  • G1 GC — современный сборщик по умолчанию (начиная с Java 9).
  • Балансирует между минимальными паузами и производительностью.
  • Делит кучу на множество небольших регионов (региональная модель).
  • Может собирать мусор выборочно по регионам, не трогая всю кучу.
  • Позволяет задавать целевую максимальную паузу, например -XX:MaxGCPauseMillis=200.
  • Флаг включения: -XX:+UseG1GC (обычно не нужен, т.к. G1 по умолчанию).

ZGC и Shenandoah

  • ZGC и Shenandoah — современные low‑latency сборщики мусора.
  • Цель — минимальные паузы (миллисекунды), даже на огромных кучах (до терабайт).
  • Работают практически полностью параллельно с приложением.
  • Требуют Java 11+ (ZGC) или Java 12+ (Shenandoah).
  • Подходят для latency‑critical систем (биржи, финтех, real‑time‑аналитика).
  • Флаги включения: -XX:+UseZGC или -XX:+UseShenandoahGC

3. Принципы работы современных GC

Молодое и старое поколение (Young/Old Generation)

JVM делит кучу на две большие части:

Молодое поколение (Young Generation): сюда попадают все новые объекты. Здесь уборка происходит часто и быстро ( Minor GC ).

Старое поколение (Old Generation, Tenured): сюда переезжают объекты, которые «выжили» несколько уборок в молодом поколении. Здесь уборка происходит реже, но дольше ( Major/Full GC ).

Почему так? Большинство объектов в Java живёт очень недолго (например, временные строки, коллекции внутри метода). Поэтому убирать молодое поколение можно быстро и часто, не трогая старое.

Minor GC

  • Очищает только молодое поколение.
  • Быстро, с короткой паузой.
  • Не затрагивает старые объекты.

Major (Full) GC

  • Очищает всю кучу (и молодое, и старое поколение).
  • Может занимать много времени (секунды и больше на больших кучах).
  • Обычно сопровождается длинной паузой приложения.

Как GC определяет, какие объекты удалить?

GC ищет «живые» объекты, начиная с корневых ссылок (root set): локальные переменные в стеках потоков, статические поля, параметры методов и т.д. Всё, до чего можно «дотянуться», считается живым. Остальное — мусор.

4. Сравнение современных сборщиков: G1, ZGC, Shenandoah

Давайте разберёмся, чем отличаются самые современные и популярные GC. Для этого наглядная таблица:

Сборщик Основная цель Модель памяти Минимальные паузы Масштабируемость Поддержка Когда использовать
G1 Баланс пауз/скорости Регионы ~10–200 мс До сотен ГБ Java 9+ (по умолч.) Большинство серверных приложений
ZGC Минимальная пауза Регионы, «цветные метки» <10 мс До терабайт Java 11+ Реальное время, latency‑critical
Shenandoah Минимальная пауза Регионы, «цветные метки» <10 мс До терабайт Java 12+ (RedHat) Реальное время, latency‑critical

G1 GC: Garbage First

  • Делит кучу на множество регионов (обычно 1–32 МБ каждый).
  • Во время уборки выбирает регионы, где больше всего мусора («garbage first»).
  • Может собирать только часть кучи, а не всю сразу.
  • Позволяет задать целевую паузу: -XX:MaxGCPauseMillis=200.
  • Подходит для баланса между скоростью и паузами; используется по умолчанию с Java 9.

Пример включения (если вдруг отключён):

java -XX:+UseG1GC -jar myapp.jar

ZGC: Z Garbage Collector

  • Экспериментальный в Java 11, стабильный с Java 15.
  • Почти не останавливает приложение: паузы обычно <10 мс, даже при 1–2 ТБ кучи.
  • Использует «цветные метки» (coloring) и особые указатели.
  • Требует 64‑битную JVM; не работает на 32‑битных системах.
  • Поддерживается на Linux, macOS, Windows.

Пример включения:

java -XX:+UseZGC -jar myapp.jar

Shenandoah

  • Разработан RedHat; цели похожи на ZGC.
  • Минимальные паузы, активная параллельная работа с приложением.
  • Поддержка Linux и Windows; часть сборок OpenJDK.
  • Использует похожие техники, но иные внутренние алгоритмы.

Пример включения:

java -XX:+UseShenandoahGC -jar myapp.jar

Визуальное сравнение

graph TD A[Молодое поколение] -->|Minor GC| B[Старое поколение] B -->|Major GC| C[GC Pause] D[G1: регионы] --> E[Выборочные регионы] F[ZGC/Shenandoah: регионы] --> G[Параллельная уборка]

5. Практика: как узнать и изменить GC

Как узнать, какой GC используется?

  1. Логи JVM: Запустите приложение с параметрами -Xlog:gc* (Java 9+) или -verbose:gc (до Java 8). В логах будет видно, какой GC используется и как часто происходят паузы.
  2. jcmd: Выполните:
    jcmd <pid> VM.flags
    
    где <pid> — идентификатор процесса Java.
  3. jvisualvm: В разделе «Мониторинг» можно посмотреть тип GC.

Как изменить GC для своего приложения?

Добавьте нужный флаг при запуске Java‑программы:

G1 GC (по умолчанию, можно указать явно):

java -XX:+UseG1GC -jar myapp.jar

ZGC:

java -XX:+UseZGC -jar myapp.jar

Shenandoah:

java -XX:+UseShenandoahGC -jar myapp.jar

Как задать размер кучи и паузы?

  • Максимальный размер кучи: -Xmx2G
  • Минимальный размер кучи: -Xms512M
  • Для G1: желаемая пауза — -XX:MaxGCPauseMillis=200

Пример полного запуска:

java -Xms512M -Xmx2G -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar myapp.jar

6. Особенности выбора GC для различных задач

Когда выбирать G1

  • В большинстве серверных и десктопных приложений — отличный выбор по умолчанию.
  • Хорошо работает на кучах от сотен мегабайт до сотен гигабайт.
  • Балансирует между скоростью и паузами.

Когда выбирать ZGC или Shenandoah

  • Если приложение чувствительно к задержкам (latency‑critical: биржи, онлайн‑игры, real‑time‑аналитика).
  • Если куча огромная (сотни гигабайт и больше).
  • Если допустимы только минимальные паузы (миллисекунды).
  • Требуются Java 11+ (ZGC) или Java 12+ (Shenandoah).

Когда хватит Parallel GC

  • Для небольших приложений, где важна максимальная пропускная способность, а паузы некритичны.
  • Для batch‑обработки, где можно «пережить» остановку на Full GC.

7. Пример: сравнение поведения GC на простом приложении

Небольшое приложение, которое генерирует много временных объектов (симуляция обработки заказов):

public class GCSimulator {
    public static void main(String[] args) {
        while (true) {
            // Создаем 100 000 объектов каждый цикл
            for (int i = 0; i < 100_000; i++) {
                String s = new String("Order-" + i);
            }
            // Немного отдыхаем
            try { Thread.sleep(100); } catch (InterruptedException e) {}
        }
    }
}

Запустите его с разными GC и посмотрите на логи:

java -Xmx256M -XX:+UseG1GC -Xlog:gc* GCSimulator
java -Xmx256M -XX:+UseZGC -Xlog:gc* GCSimulator

Что вы увидите?
G1 будет делать частые, но короткие паузы. ZGC/Shenandoah — паузы ещё короче, но могут происходить чаще. Parallel GC — паузы длиннее, но реже.

8. Типичные ошибки и нюансы при работе с GC

Ошибка №1: Ожидание, что GC решит все проблемы с памятью. GC — это не волшебная палочка. Если вы держите ссылки на ненужные объекты, никакой GC не поможет — будет утечка памяти.

Ошибка №2: Принудительный вызов System.gc(). JVM сама лучше знает, когда убирать мусор. Принудительный GC может вызвать длинную паузу и снизить производительность.

Ошибка №3: Игнорирование логов GC. Если не смотреть логи GC, можно не заметить, что ваше приложение регулярно «зависает» на Full GC.

Ошибка №4: Использование устаревших GC. Например, CMS больше не развивается. Лучше переходить на G1 или современные low‑latency сборщики.

Ошибка №5: Неправильный выбор GC для задачи. Если у вас latency‑critical приложение, а вы используете Parallel GC — ждите длинных пауз. Если у вас batch‑обработка, а вы включили ZGC — получите лишние накладные расходы.

1
Задача
JAVA 25 SELF, 64 уровень, 1 лекция
Недоступна
Узнаём используемый сборщик мусора – Детектив JVM 🕵️
Узнаём используемый сборщик мусора – Детектив JVM 🕵️
1
Задача
JAVA 25 SELF, 64 уровень, 1 лекция
Недоступна
Изменение сборщика мусора и настройка паузы – Перфекционист Настройки Производительности ⚙️
Изменение сборщика мусора и настройка паузы – Перфекционист Настройки Производительности ⚙️
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ