Сборщик Concurrent Mark Sweep (CMS) в Java VM

Сборщик мусора Concurrent Mark Sweep (CMS) предназначен для приложений, которые предпочитают более короткие паузы сбора мусора и которые могут предоставить ресурсы процессора сборщику мусора для совместного использования с приложением во время работы приложения.

Обычно приложения, которые имеют относительно большой набор долгоживущих данных (большое старое поколение) и работают на машинах с двумя или более процессорами, обычно выигрывают от использования этого сборщика. Сборщик CMS включен с параметром командной строки -XX:+UseConcMarkSweepGC.

Коллектор CMS устарел. Настоятельно рекомендуем использовать сборщик Garbage-First (G1).

Производительность и структура сборщика Concurrent Mark Sweep

Подобно другим доступным коллекторам, коллектор CMS работает с поколениями объектов; таким образом, встречаются как малые, так и большие сборки объектов. Сборщик CMS пытается сократить время пауз из-за больших сборок, используя отдельные потоки сборщика мусора для отслеживания достижимых объектов одновременно с выполнением потоков приложения.

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

Ошибка конкурентного режима

Сборщик CMS использует один или несколько потоков сборщика мусора, которые запускаются одновременно с потоками приложения с целью завершения сбора старого поколения до его заполнения.

Как описано ранее, при нормальной работе сборщик CMS выполняет большую часть своей работы по отслеживанию и развертыванию с работающими потоками приложения, поэтому потоки приложения видят только короткие паузы. Однако, если сборщик CMS не может завершить восстановление недоступных объектов до того, как старое поколение заполнится, или если выделение не может быть удовлетворено доступными блоками свободного пространства в старом поколении, то приложение приостанавливается, и сбор завершается пока все потоки приложений остановлены. Невозможность конкурентного завершения сбора называется отказом конкурентного режима и указывает на необходимость настройки параметров коллектора CMS. Если конкурентный сбор прерывается явной сборкой мусора (System.gc()) или сборкой мусора, необходимой для предоставления информации для диагностических инструментов, то сообщается о прерывании конкурентного режима.

Чрезмерное время GC и OutOfMemoryError

Сборщик CMS выдает ошибку OutOfMemoryError, если на сборку мусора уходит слишком много времени: если на сборку мусора затрачивается более 98% общего времени и восстанавливается менее 2% кучи, генерируется ошибка OutOfMemoryError.

Эта функция предназначена для предотвращения запуска приложений в течение длительного периода времени при небольшом прогрессе или его отсутствии, поскольку куча слишком мала. При необходимости эту функцию можно отключить, добавив параметр -XX:-UseGCOverheadLimit в командную строку.

Политика такая же, как и в параллельном сборщике, за исключением того, что время, затрачиваемое на выполнение конкурентных сборов, не учитывается в 98% времени. Другими словами, только сборы, выполненные в то время, когда приложение остановлено, засчитываются в чрезмерное время GC. Такие сборки обычно происходят из-за сбоя конкурентного режима или явного запроса сбора (например, вызова System.gc()).

Сборщик Concurrent Mark Sweep и плавающий мусор

Сборщик CMS, как и все другие сборщики в Java HotSpot VM, является сборщиком трассировки, который идентифицирует по крайней мере все достижимые объекты в куче.

Поскольку потоки приложения и поток сборщика мусора выполняются конкурентно во время основной сборки мусора, объекты, которые отслеживаются потоком сборщика мусора, могут впоследствии стать недоступными к моменту окончания процесса сбора мусора. Такие недоступные объекты, которые еще не были возвращены, называются плавающим мусором. Количество плавающего мусора зависит от продолжительности цикла одновременного сбора и от частоты обновления ссылки, также известного как мутации, приложением. Кроме того, поскольку молодое поколение и старое поколение собираются независимо, каждое из них служит источником корней для другого. В качестве приблизительной рекомендации попробуйте увеличить размер старого поколения на 20%, чтобы учесть плавающий мусор. Плавающий мусор в куче в конце одного конкурентного цикла сбора собирается во время следующего цикла сбора.

Паузы сборщика Concurrent Mark Sweep

Сборщик CMS приостанавливает приложение дважды во время конкурентного цикла сбора. Первая пауза - пометить как живые объекты, непосредственно доступные из корней (например, ссылки на объекты из стеков и регистров потоков приложений, статических объектов и т. д.) и из других частей кучи (например, молодого поколения).

Эта первая пауза называется начальной паузой метки (initial mark pause). Вторая пауза наступает в конце фазы конкурентной трассировки и находит объекты, которые были пропущены при конкурентной трассировке из-за обновлений потоками приложения в объекте после того, как сборщик CMS завершил трассировку этого объекта. Эта вторая пауза называется паузой примечания (remark pause).

Конкурентные фазы сборщика Concurrent Mark Sweep

Конкурентная трассировка графа достижимых объектов происходит между начальной паузой отметки и паузой примечания.

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

Начало конкурентного цикла сбора

С помощью последовательного коллектора основная коллекция создается всякий раз, когда старое поколение заполняется, и все потоки приложения останавливаются во время сбора. Напротив, запуск конкурентной сборки в сборщике CMS должен быть рассчитан так, чтобы сборка могла завершиться до того, как старое поколение заполнится; в противном случае приложение будет наблюдать более длинные паузы из-за сбоя конкурентного режима. Есть несколько способов начать конкурентную сборку.

Основываясь на недавней истории, сборщик CMS поддерживает оценки времени, оставшегося до того, как старое поколение будет исчерпано, и времени, необходимого для конкурентного цикла сбора. Используя эти динамические оценки, запускается конкурентноый цикл сбора с целью завершения цикла сбора до того, как старое поколение будет исчерпано. Эти оценки дополняются для безопасности, потому что отказ конкурентного режима может быть очень дорогостоящим.

Конкурентный сбор также начинается, если занятость старого поколения превышает первоначальную занятость (процент от старого поколения). Значение по умолчанию для этого начального порога занятости составляет приблизительно 92%, но это значение может изменяться от релиза к релизу. Это значение можно настроить вручную с помощью параметра командной строки -XX:CMSInitiatingOccupancyFraction=<N>, где <N> - это целое значение в процентах (от 0 до 100) от размера старого поколения.

Планирование пауз

Паузы для сборки молодого поколения и сборки старого поколения происходят независимо.

Они не перекрываются, но могут происходить в быстрой последовательности, так что пауза от одной сборки, за которой сразу следует одна от другой сборки, может выглядеть как одна более длинная пауза. Чтобы избежать этого, сборщик CMS пытается запланировать паузу примечания примерно посередине между предыдущим и следующим паузами молодого поколения. Это планирование в настоящее время не выполняется для начальной паузы отметки, которая обычно намного короче, чем пауза примечания.

Измерения сборки Concurrent Mark Sweep

Ниже приведены выходные данные сборщика CMS с параметром -Xlog:gc:

[121,834s][info][gc] GC(657) Pause Initial Mark 191M->191M(485M) (121,831s, 121,834s) 3,433ms
[121,835s][info][gc] GC(657) Concurrent Mark (121,835s)
[121,889s][info][gc] GC(657) Concurrent Mark (121,835s, 121,889s) 54,330ms
[121,889s][info][gc] GC(657) Concurrent Preclean (121,889s)
[121,892s][info][gc] GC(657) Concurrent Preclean (121,889s, 121,892s) 2,781ms
[121,892s][info][gc] GC(657) Concurrent Abortable Preclean (121,892s)
[121,949s][info][gc] GC(658) Pause Young (Allocation Failure) 324M->199M(485M) (121,929s, 121,949s) 19,705ms
[122,068s][info][gc] GC(659) Pause Young (Allocation Failure) 333M->200M(485M) (122,043s, 122,068s) 24,892ms
[122,075s][info][gc] GC(657) Concurrent Abortable Preclean (121,892s, 122,075s) 182,989ms
[122,087s][info][gc] GC(657) Pause Remark 209M->209M(485M) (122,076s, 122,087s) 11,373ms
[122,087s][info][gc] GC(657) Concurrent Sweep (122,087s)
[122,193s][info][gc] GC(660) Pause Young (Allocation Failure) 301M->165M(485M) (122,181s, 122,193s) 12,151ms
[122,254s][info][gc] GC(657) Concurrent Sweep (122,087s, 122,254s) 166,758ms
[122,254s][info][gc] GC(657) Concurrent Reset (122,254s)
[122,255s][info][gc] GC(657) Concurrent Reset (122,254s, 122,255s) 0,952ms
[122,297s][info][gc] GC(661) Pause Young (Allocation Failure) 259M->128M(485M) (122,291s, 122,297s) 5,797ms

Примечание. Выходные данные для сбора CMS (GC ID 657) перемежаются с выходными данными из малых сборок (GC ID 658, 659 и 660); как правило, многие малые сборки происходят во время конкурентного цикла сбора. Pause Initial Mark указывает начало конкурентного цикла сбора. Строки, начинающиеся с "Concurent", указывают начало и конец конкурентных фаз. Pause Remark - это последняя пауза. Ранее не обсуждались этапы предварительной очистки. Предварительная очистка представляет собой работу, которая может выполняться одновременно при подготовке к этапу примечания. Последняя фаза указана в Concurrent Reset и находится в стадии подготовки к следующей конкурентной сборки.

Начальная пауза метки обычно коротка относительно времени паузы малого сбора. Конкурентные фазы (конкурентная метка, конкурентная предварительная очистка и конкурентная очистка) обычно длятся значительно дольше, чем малая пауза сбора, как указано в примере выходных данных сборщика CMS. Обратите внимание, что приложение не приостанавливается на этих одновременных этапах. Пауза примечания часто сопоставима по длине с малой сборкой. На паузу примечания влияют определенные характеристики приложения (например, высокая скорость модификации объекта может увеличить эту паузу) и время, прошедшее с момента последней малой сборки (например, большее количество объектов в молодом поколении может увеличить эту паузу).


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки