Shenandoah GC в JDK
Привет, Хабр!
Shenandoah GC — это сборщик мусора для OpenJDK, целью которого является минимизация времени пауз, возникающих в результате сборки мусора, путем выполнения процессов эвакуации памяти параллельно с работающими Java‑потоками. Фичей Shenandoah является то, что время пауз GC не зависит от размера кучи, что означает одинаково короткие времена пауз как для кучи размером в 200 МБ, так и для 200 ГБ. Это достигается за счет использования дополнительного указателя косвенности для каждого Java‑объекта, что позволяет потокам GC компактизировать кучу, пока Java‑потоки продолжают выполняться.
Разработка Shenandoah началась с экспериментального проекта, целью которого было предоставление альтернативы другим сборщикам мусора, которые приоритизируют пропускную способность или размер памяти над отзывчивостью. Уже к 12-й версии JDK Shenandoah был готов к использованию, хотя и оставался помеченным как экспериментальная функция. Это состояние сохранялось, чтобы соответствовать статусу других сборщиков мусора, таких как Epsilon GC и ZGC.
Основное событие в истории Shenandoah — включение его в состав продуктовых функций начиная с 15-й версии JDK. Т.е для его использования больше не требовалось разблокировать экспериментальные опции VM. Такое изменение было в основном косметическим и касалось классификации настроек Shenandoah. Тем не менее, это был значительный шаг вперед, подтверждающий зрелость этого сборщика мусора.
Фазы работы Shenandoah GC
Основные фазы работы Shenandoah GC включают:
Инициация маркировки: запускает конкурентную маркировку, подготавливает кучу и потоки приложения для конкурентной маркировки, а затем сканирует корневой набор. Это первая пауза в цикле, и основным потребителем времени является сканирование корневого набора.
Конкурентная маркировка: проходит по куче и отслеживает достижимые объекты. Фаза выполняется параллельно с приложением, и ее продолжительность зависит от кол-во живых объектов и структуры графа объектов в куче.
Финальная маркировка: завершает конкурентную маркировку, опорожняя все ожидающие очереди маркировки/обновления и повторно сканируя корневой набор. Также инициирует эвакуацию, определяя регионы для эвакуации.
Конкурентная очистка: восстанавливает регионы кучи, которые полностью свободны от живых объектов, обнаруженных после конкурентной маркировки.
Конкурентная эвакуация: копирует объекты из набора коллекций в другие регионы. Эта фаза также выполняется параллельно с приложением, и ее продолжительность зависит от размера выбранного набора коллекций для цикла.
Инициация обновления ссылок: инициирует фазу обновления ссылок, подготавливая GC для следующей фазы. Это третья пауза в цикле, самая короткая из всех.
Конкурентное обновление ссылок: проходит по куче и обновляет ссылки на объекты, которые были перемещены во время конкурентной эвакуации. Фаза выполняется параллельно с приложением.
Финальное обновление ссылок: завершает фазу обновления ссылок, повторно обновляя существующий корневой набор. Также перерабатывает регионы из набора коллекций.
Конкурентная очистка: восстанавливает регионы набора коллекций, которые теперь не имеют ссылок.
Настройка Shenandoah GC в JVM
Основные параметры настройки Shenandoah GC в JVM:
-XX:+UseShenandoahGC: параметр включает использование Shenandoah GC. Пример использования: java -XX:+UseShenandoahGC MyApplication
.
-XX:ShenandoahGCHeuristics: управляет эвристикой Shenandoah. Например, aggressive
для уменьшения времени пауз за счет увеличения использования CPU и памяти. Пример использования: java -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive MyApplication
.
-XX:ShenandoahFreeThreshold: задает процент свободной памяти в куче после сборки мусора. Пример использования: java -XX:+UseShenandoahGC -XX:ShenandoahFreeThreshold=20 MyApplication
.
-XX:ShenandoahAllocationThreshold: устанавливает порог для начала сборки мусора на основе процента занятой памяти в куче. Пример использования: java -XX:+UseShenandoahGC -XX:ShenandoahAllocationThreshold=60 MyApplication
.
-XX:ShenandoahGarbageThreshold: определяет процент мусора в куче, при достижении которого начнется сборка мусора. Пример использования: java -XX:+UseShenandoahGC -XX:ShenandoahGarbageThreshold=20 MyApplication
.
ShenandoahUncommitDelay: параметр определяет задержку (в мс) перед тем, как Shenandoah GC освободит память, которая была выделена для кучи, но в данный момент не используется. По умолчанию значение этого параметра равно 1000 мс. Пример использования: java -XX:+UseShenandoahGC -XX:ShenandoahUncommitDelay=500 MyApplication
(устанавливает задержку в 500 мс).
ShenandoahGCMode: параметр позволяет выбрать режим работы Shenandoah GC. Возможные значения включают:
normal
: стандартный режим работы.
iu
: (Immediate Update) режим, в котором обновление ссылок происходит сразу после копирования объекта.
satb
: (Snapshot At The Beginning) режим, в котором используется алгоритм снимка состояния кучи в начале фазы маркировки.
Запуск сборщика
Добавляем параметр -XX:+UseShenandoahGC
в командную строку при запуске приложения:
java -XX:+UseShenandoahGC -jar my-application.jar
Настроим сборщик так:
java -XX:+UseShenandoahGC \
-XX:+UnlockExperimentalVMOptions \
-XX:ShenandoahGCHeuristics=adaptive \
-XX:+AlwaysPreTouch \
-Xms4G -Xmx4G \
-jar my-application.jar
-XX:+UseShenandoahGC: включает использование Shenandoah GC.
-XX:+UnlockExperimentalVMOptions: разблокирует экспериментальные опции VM
-XX:ShenandoahGCHeuristics=adaptive: устанавливает эвристику Shenandoah на «адаптивную», что позволяет сборщику мусора автоматически адаптироваться к различным условиям работы приложения.
-XX:+AlwaysPreTouch: заставляет JVM заранее выделить и инициализировать всю память кучи при запуске.
-Xms4G -Xmx4G: устанавливает начальный и максимальный размер кучи в 4 гигабайта.
Сравнение с другими сборщиками
Параметр / Сборщик | Shenandoah | G1 | Parallel GC | Serial GC | CMS |
---|---|---|---|---|---|
Алгоритм | Разделение поколений, эвакуация областей | Разделение поколений, эвакуация областей | Остановка-мира | Остановка-мира | Параллельная отметка, параллельная очистка |
Паузы | Короткие, не зависят от размера кучи | Короткие, могут увеличиваться с размером кучи | Относительно короткие, зависят от размера кучи | Долгие, зависят от размера кучи | Короткие, не зависят от размера кучи |
Производительность | Высокая, особенно при больших кучах | Хорошая, но может ухудшаться при больших кучах | Высокая, но сбои при сборке мусора | Низкая, используется в основном для маленьких приложений | Хорошая, но возможны задержки из-за сборки мусора |
Применение | Подходит для приложений с большим объемом памяти и требованиями к низким паузам | Подходит для приложений с большим объемом памяти и умеренными требованиями к паузам | Хорош для многопоточных приложений с большим объемом памяти | Подходит для простых или однопоточных приложений | Подходит для приложений с низкими требованиями к паузам |
Сборщик мусора Shenandoah подходит для приложений, где важны короткие паузы на сборку мусора, независимо от размера кучи.
Больше практических инструментов Java-разработки эксперты из OTUS рассматривают в рамках практического онлайн-курса. Узнать о курсе подробнее можно по ссылке.