Параллельный сборщик мусора в Java

Параллельный сборщик (parallel collector) (также называемый сборщиком пропускной способности) является сборщиком поколений, подобным последовательному сборщику. Основное различие между последовательным и параллельным сборщиками состоит в том, что параллельный сборщик имеет несколько потоков, которые используются для ускорения сбора мусора.

Параллельный сборщик включается параметром командной строки -XX:+UseParallelGC. По умолчанию при использовании этой опции как малые, так и большие сборки объектов выполняются параллельно, чтобы еще больше снизить затраты на сборку мусора.

Количество потоков параллельного сборщика мусора

На машине с <N> аппаратными потоками, где <N> больше 8, параллельный сборщик использует фиксированную долю <N> в качестве числа потоков сборщика мусора.

Доля составляет приблизительно 5/8 для больших значений <N>. При значениях <N> ниже 8 используемое число составляет <N>. На выбранных платформах доля падает до 5/16. Конкретное количество потоков сборщика мусора можно настроить с помощью параметра командной строки (который будет описан ниже). На хосте с одним процессором параллельный сборщик, скорее всего, не будет работать так же хорошо, как последовательный сборщик из-за накладных расходов, необходимых для параллельного выполнения (например, синхронизации). Однако при запуске приложений с кучами среднего и большого размера он обычно значительно уступает последовательному сборщику на компьютерах с двумя процессорами и обычно работает значительно лучше, чем последовательный сборщик, когда доступно более двух процессоров.

Количество потоков сборщика мусора можно контролировать с помощью параметра командной строки -XX:ParallelGCThreads=<N>. Если вы настраиваете кучу с помощью параметров командной строки, то размер кучи, необходимый для хорошей производительности с параллельным сборщиком, такой же, как и для последовательного сборщика. Однако включение параллельного сборщика должно сделать паузы сбора короче. Поскольку несколько потоков сборщика мусора участвуют в малом сборе, возможна некоторая фрагментация благодаря продвижению молодого поколения к старому поколению во время сбора. Каждый поток сбора мусора, участвующий в малой сборке, резервирует часть старого поколения для продвижения, и разделение доступного пространства на эти "буферы продвижения" может вызвать эффект фрагментации. Уменьшение количества потоков сборщика мусора и увеличение размера старого поколения уменьшит этот эффект фрагментации.

Расположение поколений в параллельных сборщиках

Расположение поколений отличается в параллельном сборщике.

Это расположение показано на рисунке:


Эргономика параллельного сборщика

Когда параллельный коллектор выбирается с помощью -XX:+UseParallelGC, он включает метод автоматической настройки, который позволяет указывать поведение вместо размеров поколений и других низкоуровневых параметров настройки.

Параметры для определения поведения параллельного сборщика

Вы можете указать максимальное время приостановки сбора мусора, пропускную способность и площадь (размер кучи).

  • Максимальное время приостановки сбора мусора. Максимальное время приостановки задается с помощью параметра командной строки -XX:MaxGCPauseMillis=<N>. Это интерпретируется как подсказка, что желательны времена паузы <N> миллисекунд; по умолчанию нет максимального времени паузы. Если задана цель времени паузы, размер кучи и другие параметры, относящиеся к сборке мусора, корректируются в попытке сделать паузы сбора мусора короче указанного значения; однако желаемая цель паузы не всегда может быть достигнута. Эти корректировки могут привести к тому, что сборщик мусора уменьшит общую пропускную способность приложения.
  • Пропускная способность: Цель пропускной способности измеряется с точки зрения времени, затраченного на сборку мусора, по сравнению с временем, потраченным вне сбора мусора, которое называется временем приложения. Цель задается параметром командной строки -XX:GCTimeRatio=<N>, который устанавливает отношение времени сбора мусора к времени приложения в 1/(1+<N>).

    Например, -XX:GCTimeRatio=19 устанавливает цель в 1/20 или 5% от общего времени сбора мусора. Значение по умолчанию - 99, что приводит к цели в 1% времени в сборке мусора.

  • Footprint (площадь): максимальный размер кучи определяется параметром -Xmx<N>. Кроме того, сборщик имеет неявную цель минимизировать размер кучи, пока выполняются другие цели.

Приоритет целей параллельного сборщика

Цели - это максимальное время паузы, цель пропускной способности и минимальное количество места, и цели решаются в следующем порядке:

Максимальное время паузы достигается первым. Только после того, как это достигнуто, решается цель пропускной способности. Точно так же, только после того, как первые две цели были достигнуты, рассматривается цель площади.

Регулировка размера поколений параллельного сборщика

Такие статистические данные, как среднее время паузы, сохраняемое сборщиком, обновляются в конце каждой сборки.

Затем выполняются тесты для определения того, были ли достигнуты цели, и вносятся необходимые корректировки в размер поколения. Исключением является то, что явные сборки мусора, например, вызовы System.gc(), игнорируются с точки зрения хранения статистики и корректировки размеров поколений.

Увеличение и уменьшение размера поколения осуществляется с помощью приращений, которые являются фиксированным процентом от размера поколения, так что поколение поднимается или опускается до желаемого размера. Рост и сжатие осуществляются с разной скоростью. По умолчанию поколение увеличивается с шагом 20% и уменьшается с шагом 5%. Процент роста определяется параметром командной строки -XX:YoungGenerationSizeIncrement=<Y> для молодого поколения и -XX:TenuredGenerationSizeIncrement=<T> для старого поколения. Процент сжатия, с которым генерируется поколение, корректируется с помощью флага командной строки -XX:AdaptiveSizeDecrementScaleFactor=<D>. Если прирост роста составляет X%, то уменьшение при сжатии составляет X/D%.

Если сборщик решает вырастить поколение при запуске, то в приращение добавляется дополнительный процент. Это дополнение распадается с количеством сборок и не имеет долгосрочного эффекта. Целью дополнения является повышение производительности при запуске. Там нет дополнения к проценту для сокращения.

Если максимальное время паузы не достигается, то размер только одного поколения сокращается за раз. Если время паузы обоих поколений выше цели, то размер поколения с большим временем паузы сокращается первым.

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

Размер кучи параллельного сборщика по умолчанию

Если в командной строке не указаны начальный и максимальный размеры кучи, они рассчитываются на основе объема памяти на компьютере. Максимальный размер кучи по умолчанию составляет одну четвертую от физической памяти, а начальный размер кучи составляет 1/64 от физической памяти. Максимальный объем пространства, выделенного молодому поколению, составляет одну треть от общего размера кучи.

Спецификация начального и максимального размера кучи параллельного сборщика

Вы можете указать начальный и максимальный размеры кучи, используя опции -Xms (начальный размер кучи) и -Xmx (максимальный размер кучи).

Если вы знаете, сколько кучи должно работать для вашего приложения, вы можете установить -Xms и -Xmx в одно и то же значение. Если вы не знаете, тогда JVM запустится с использованием начального размера кучи, а затем увеличит кучи Java, пока не найдет баланс между использованием кучи и производительностью.

Другие параметры и опции могут влиять на эти значения по умолчанию. Чтобы проверить значения по умолчанию, используйте параметр -XX:+PrintFlagsFinal и найдите -XX:MaxHeapSize в выходных данных. Например, в Linux вы можете запустить следующее:

java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize

Избыточное время параллельного сборщика и OutOfMemoryError

Параллельный сборщик создает OutOfMemoryError, если слишком много времени тратится на сборку мусора (GC).

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


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


Комментарии

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

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

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

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