--- aliases: - G1 tags: - зрелость/🌱 date: "[[2023-11-06]]" zero-link: - "[[../../../../garden/ru/meta/zero/00 Java разработка]]" parents: - "[[../../../../garden/ru/dev/java/gc/Garbage Collector]]" linked: --- Можно сказать, что этот сборщик пришел на замену [CMS](Concurrent%20Mark%20Sweep.md) в [Java 9](Java%209.md). Также, как и остальные сборщики использует подход [Generational Collection](Generational%20Collection.md), однако Heap разделяется не на большие части Young, Old, а на небольшие равные регионы. Размер регионов по умолчанию 1 mb, но можно указать произвольное значение до 32 mb. Общее количество регионов в куче равно размеру кучи, деленному на размер региона. При настройке G1 рекомендуется ориентироваться на 2048 регионов, чтобы G1 работал эффективно. И уже каждый сегмент может быть помечен как: - eden - новые объекты, тут происходит [аллокация](Аллокация.md). - survivor - объекты пережившие несколько сборок - old - долгоживущие объекты ![](Pasted%20image%2020231107221815.png) При этом у нас нет фиксированного размера под поколение. Каждый регион может отвечать за хранение молодых или старых объектов в разные моменты времени, что обеспечивает большую гибкость. > [!INFO] Большой объект (humongous) > Если объект занимает 2 и более сегмента, то он не перемещается за исключением Full GC. И в тот же сегмент не добавляются другие объекты, даже если у вас хватает места в регионе. > > ![](Pasted%20image%2020231107164540.png) > > В [[Java 11]] были внесены изменения в обработку больших объектов. ## Minor Collection Minor сборка обычно происходит, когда eden заполняется. Над [minor](../../../../garden/ru/dev/java/gc/Garbage%20Collector.md#^minor) сборками мусора работают несколько потоков. Но очистка выполняется не на всем поколении, а только на регионах молодого поколения. За счет этого количество [StopTheWorld](StopTheWorld.md) становится больше, но сборка выполняется быстрее и не превышает желаемое время. ## Mixed Collection Mixed сборка обрабатывает как молодое поколение, так и часть старого поколения. Эта запускается после определенного количества minor сборок. В G1 существует процесс, называемый Marking Cycle, который работает параллельно с основным приложением и составляет список живых объектов. Он напоминает работу сборщика [CMS](Concurrent%20Mark%20Sweep.md). - **Initial mark.** Пометка [GC Root](../../../../garden/ru/dev/java/gc/Garbage%20Collector.md#^gcroot) с использованием информации от [minor](../../../../garden/ru/dev/java/gc/Garbage%20Collector.md#^minor) сборок. - В [Java 10](Java%2010.md) были внедрены улучшения. Параллельная пометка и улучшенное использование ресурсов процессора. - **Concurrent marking.** Пометка всех живых объектов в heap. Работает в нескольких потоках. - **Remark.** Дополнительный поиск не учтенных ранее живых объектов. Используется [StopTheWorld](StopTheWorld.md) - **Cleanup.** Очистка вспомогательных структур учета ссылок на объект и поиск пустых регионов, которые уже можно использовать для размещения новых объектов. Первая часть этого шага выполняется с использованием [StopTheWorld](StopTheWorld.md). Следует иметь в виду, что для получения списка живых объектов G1 использует алгоритм [Snapshot at the beginning (SATB)](Snapshot%20at%20the%20beginning%20(SATB).md), то есть в список живых попадают все объекты, которые были таковыми на момент начала работы алгоритма, плюс все объекты, созданные за время его выполнения. Это, в частности, означает, что G1 допускает наличие плавающего мусора. После окончания цикла пометки G1 переключается на выполнение **смешанных сборок**. Это значит, что при каждой сборке к набору регионов младшего поколения, подлежащих очистке, добавляется некоторое количество регионов старшего поколения. Количество таких сборок и количество очищаемых регионов старшего поколения выбирается исходя из имеющейся у сборщика статистики о предыдущих сборках таким образом, чтобы не выходить за требуемое время сборки. Как только сборщик очистил достаточно памяти, он переключается обратно в режим minor сборок. Очередной цикл пометки и, как следствие, очередные смешанные сборки будут запущены тогда, когда наполненность кучи превысит определенный порог. ## Full GC Может оказаться так, что в процессе очистки памяти в куче не остается свободных регионов, в которые можно было бы копировать выжившие объекты. Это приводит к возникновению ситуации allocation (evacuation) failure. В таком случае сборщик выполняет полную сборку мусора по всей куче при остановленных основных потоках приложения. С [[Java 9]] была реализована параллельная сборка мусора в этом режиме. Опираясь на уже упомянутую статистику о предыдущих сборках, G1 может менять количество регионов, закрепленных за определенным поколением, для оптимизации будущих сборок. ## Дефрагментация G1 копирует объекты из одной или нескольких областей кучи в одну область кучи, при этом одновременно сжимая и освобождая память. То есть используется подход [Copy Collector](Copy%20Collector.md). Этот процесс выполняется параллельно, чтобы уменьшить время пауз и увеличить пропускную способность. Таким образом, при каждой сборке мусора G1 постоянно работает над уменьшением фрагментации, работая в пределах заданного пользователем времени паузы. Сборщик мусора CMS не выполняет дефрагментацию. Сборка мусора Parallel GC выполняет только сжатие всей кучи, что приводит к значительному времени паузы. ## Remembered Set ![](Pasted%20image%2020231108073803.png) - Хранит информацию о местонахождении ссылок на объекты из региона - Из старого в молодое поколение - Между регионами в старом поколении - Позволяет собирать регионы независимо ## Ситуации [StopTheWorld](StopTheWorld.md) Если резюмировать, то у G1 мы получаем STW в следующих случаях: 1. Процессы переноса объектов между поколениями. Для минимизации таких пауз G1 использует несколько потоков, то есть [Parallel Collection](Parallel%20Collection.md) 2. Короткая фаза начальной пометки корней в рамках цикла пометки. 3. Более длинная пауза в конце фазы remark и в начале фазы cleanup цикла пометки. ## Работа с [барьерами](Барьеры%20в%20программировании.md) Для работы G1 нужны барьеры 2 типов. Используются они для поддержания согласованности heap в момент сборки мусора. - Pre-barrier - Работает только, когда идет фоновая сборка мусора. - Сохраняет старое значение поля - Поддерживается корректность фоновой маркировки - [SATB](../../../../garden/ru/dev/java/gc/Garbage%20Collector.md#^satb) - Post-barrier - Поддержка актуальности Remembered Set - Не срабатывают, когда мы модифицируем указатель из молодого объекта в молодой Результат сохраняется в буферы, локальные для потока. В процессе фоновой сборки происходит обработка. ## Выводы **Минусы:** - Очень плохо работает с объектами, которые занимают в памяти больше 1 mb. - Не является сборщиком мусора в реальном времени. Имеются [StopTheWorld](StopTheWorld.md). - Потребляет много ресурсов CPU, до 10% от всего потребления приложением. **Плюсы:** - Фоновый и параллельный. - Ожидаемое, но не гарантированное, время пауз [StopTheWorld](StopTheWorld.md). - Слабо подвержен фрагментации. **Когда использовать:** - Нужна хорошая производительность - Изначально позиционировался для приложений, размер heap которых больше 4-5 Gb - Продолжительность пауз 0,5-1 s - Минимальная настройка - Занятость кучи больше 50% - Скорость создания объектов серьезно варьируется **Когда не использовать:** - Жесткие требования на паузы меньше 100 мс - Требуется максимально возможный throughput даже в ущерб latency **Как включить:** - `-XX:+UseG1GC` - включение сборщика - `-XX:MaxGCPauseMillis=` максимальная пауза для сборки мусора - `-XX:GCPauseIntervalMills=` интервал между паузами ## Дополнительный материал - [Getting Started with the G1 Garbage Collector](https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html) - Документация Oracle. - [Владимир Иванов — G1 Garbage Collector - YouTube](https://www.youtube.com/watch?v=iGRfyhE02lA)