Алгоритмы сборки мусора Java [до Java 9]

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

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

Жизненный цикл объекта Java можно разделить на 3 этапа:

Создание объекта

Чтобы создать объект, обычно мы используем ключевое слово new. Например:

Object obj = new Object();

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

  • Объект в использовании

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

  • Уничтожение объектов

    Система сбора мусора отслеживает объекты и, по возможности, подсчитывает количество ссылок на каждый объект. Когда нет ссылок на объект, нет способа добраться до него с помощью текущего кода, поэтому имеет смысл освободить связанную с ним память.

 

Алгоритмы сбора мусора

Создание объектов выполняется кодом, который вы пишете; и фреймворками, которые вы используете для использования их предоставляемых функций. Как разработчик Java, мы не обязаны освобождать память или разыменовывать объекты. Это делается автоматически на уровне JVM сборщиком мусора. С момента появления Java было много обновлений алгоритмов, которые работают за сценой для освобождения памяти. Давайте посмотрим, как они работают?

Отметить и подмести

Это начальный и очень простой алгоритм, который выполняется в два этапа:

  1. Маркировка живых объектов – найдите все объекты, которые еще живы.
  2. Удаление недоступных объектов – избавьтесь от всего остального – предположительно мертвых и неиспользуемых объектов.

Для начала GC определяет некоторые конкретные объекты как корни сбора мусора. Например, локальные переменные и входные параметры текущих выполняемых методов, активные потоки, статические поля загруженных классов и ссылки JNI. Теперь GC обходит весь граф объектов в вашей памяти, начиная с этих корней и следуя ссылкам от корней к другим объектам. Каждый объект, который посещает GC, помечается как живой.

Потоки приложения должны быть остановлены для того, чтобы маркировка произошла, поскольку она не может реально пройти по графу, если он продолжает меняться. Это называется Stop The World pause.

Второй этап — избавление от неиспользуемых объектов для освобождения памяти. Это можно сделать разными способами, например

  • Обычное удаление – Обычное удаление удаляет неиспользуемые объекты, чтобы освободить место и оставляет ссылаемые объекты и указатели. Распределитель памяти(вид хеш-таблицы) хранит ссылки на блоки свободного пространства, где может быть выделен новый объект.Его часто называют алгоритмом «марк-зачистка».
    Обычное удаление — пометить и очистить

Обычное удаление – пометить и очистить

  • Удаление со сжатием — удаление только неиспользуемых объектов неэффективно, поскольку блоки свободной памяти разбросаны по всей области хранения и вызывают ошибку OutOfMemoryError, если созданный объект достаточно большой и не находит достаточно большой блок памяти.Чтобы решить эту проблему, после удаления неиспользуемых объектов выполняется сжатие оставшихся ссылочных объектов. Здесь сжатие относится к процессу перемещения ссылочных объектов вместе. Это делает новое распределение памяти намного проще и быстрее.Его часто называют алгоритмом «маркировка-зачистка-компактность».
    Удаление с уплотнением

    Удаление с уплотнением

  • Удаление с копированием – очень похоже на подход маркировки и сжатия, поскольку они тоже перемещают все живые объекты. Важное отличие в том, что целью перемещения является другая область памяти.Его часто называют алгоритмом маркировки-копирования.
    Удаление с копированием — Mark and Sweep

    Удаление с копированием – Mark and Sweep

Прежде чем читать дальше, я искренне советую вам сначала прочитать java memory management. Там довольно подробно рассказывается о молодом поколении, старом поколении и постоянном поколении.

Сборка мусора Concurrent Mark Sweep(CMS)

Сборка мусора CMS по сути является модернизированным методом пометки и очистки. Он сканирует память кучи с использованием нескольких потоков. Он был модифицирован для использования преимуществ более быстрых систем и имел улучшения производительности.

Он пытается минимизировать паузы, вызванные сборкой мусора, выполняя большую часть работы по сборке мусора одновременно с потоками приложения. Он использует параллельный алгоритм stop-the-world mark-copy в Young Generation и в основном параллельный алгоритм mark-sweep в Old Generation.

Чтобы использовать CMS GC, используйте следующий аргумент JVM:

-XX:+UseConcMarkSweepGC

Параметры оптимизации CMS GC

ФлагОписание
-XX:+UseCMSInitiating\OccupancyOnlyУказывает, что вы хотите использовать исключительно занятость в качестве критерия для начала операции по сбору CMS.
-XX:CMSInitiating\OccupancyFraction=70Устанавливает процент занятости генерации CMS для начала цикла сбора CMS.
-XX:CMSTriggerRatio=70Это процент MinHeapFreeRatio в генерации CMS, который выделяется до начала цикла CMS.
-XX:CMSTriggerPermRatio=90Устанавливает процент MinHeapFreeRatio в постоянной генерации CMS, который выделяется перед началом цикла сбора CMS.
-XX:CMSWaitDuration=2000Используйте этот параметр, чтобы указать, как долго CMS может ожидать сбора молодых растений.
-XX:+UseParNewGCВыбирает использование параллельного алгоритма для сбора молодого пространства.
-XX:+CMSConcurrentMTEnabledПозволяет использовать несколько потоков для параллельных фаз.
-XX:ConcGCThreads=2Устанавливает количество параллельных потоков, используемых для одновременных фаз.
-XX:ParallelGCThreads=2Устанавливает количество параллельных потоков, которые вы хотите использовать для фаз остановки мира.
-XX:+CMSIncrementalModeВключить режим инкрементальной CMS(iCMS).
-XX:+CMSClassUnloadingEnabledЕсли эта опция не включена, CMS не будет очищать постоянное пространство.
-XX:+ExplicitGCInvokes\ConcurrentЭто позволяет System.gc() запускать параллельную сборку вместо полного цикла сборки мусора.

Последовательный сбор мусора

Этот алгоритм использует mark-copy для молодого поколения и mark-sweep-compact для старого поколения. Он работает в одном потоке. При выполнении он замораживает все остальные потоки до тех пор, пока не будут завершены операции по сборке мусора.

Из-за того, что последовательная сборка мусора замораживает потоки, она применима только для очень маленьких программ.

Чтобы использовать последовательный сборщик мусора, используйте следующий аргумент JVM:

-XX:+UseSerialGC

Параллельная сборка мусора

Подобно последовательному GC, он использует mark-copy в молодом поколении и mark-sweep-compact в старом поколении. Несколько параллельных потоков используются для фаз маркировки и копирования/уплотнения. Вы можете настроить количество потоков с помощью параметра -XX:ParallelGCThreads=N.

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

До Java 8 мы видели Parallel GC как сборщик мусора по умолчанию. Начиная с Java 9, G1 является сборщиком мусора по умолчанию на 32- и 64-битных конфигурациях сервера. – JEP [248]

Чтобы использовать параллельный GC, используйте следующий аргумент JVM:

-XX:+UseParallelGC

Сбор мусора G1

Сборщик мусора G1(Garbage First) был доступен в Java 7 и разработан как долгосрочная замена сборщика CMS. Сборщик мусора G1 — это параллельный, конкурентный и инкрементально уплотняющий сборщик мусора с низкой паузой.

Этот подход подразумевает сегментацию кучи памяти на несколько небольших регионов(обычно 2048). Каждый регион помечается либо как молодое поколение(далее делится на регионы эдема или регионы выживших), либо как старое поколение. Это позволяет GC избегать сбора всей кучи сразу, а вместо этого подходить к проблеме постепенно. Это означает, что за один раз рассматривается только подмножество регионов.

Области памяти отмечены - G1
Области памяти отмечены – G1

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

Как и другие алгоритмы, к сожалению, операция уплотнения происходит с использованием подхода Stop the World. Но в соответствии с его целью разработки вы можете задать для него определенные цели производительности. Вы можете настроить длительность пауз, например, не более 10 миллисекунд в любую заданную секунду. Garbage-First GC сделает все возможное, чтобы достичь этой цели с высокой вероятностью(но не с уверенностью, это было бы сложно в реальном времени из-за управления потоками на уровне ОС).

Если вы хотите использовать машины Java 7 или Java 8, используйте аргумент JVM, как показано ниже:

-XX:+UseG1GC

Параметры оптимизации G1

ФлагОписание
-XX:G1HeapRegionSize=16mРазмер области кучи. Значение будет степенью двойки и может варьироваться от 1 МБ до 32 МБ. Цель — иметь около 2048 областей на основе минимального размера кучи Java.
-XX:МаксГЦПаузаМиллис=200Устанавливает целевое значение для желаемого максимального времени паузы. Значение по умолчанию — 200 миллисекунд. Указанное значение не адаптируется к размеру вашей кучи.
-XX:G1ReservePercent=5Это определяет минимальный резерв в куче.
-XX:G1Процент Уверенности=75Это эвристика прогнозирования паузы коэффициента уверенности.
-XX:GCPauseIntervalMillis=200Это интервал времени паузы на MMU в миллисекундах.

Параметры настройки ГХ

Флаги конфигурации GC

ФлагОписание
-Xms2048m -Xmx3gУстанавливает начальный и максимальный размер кучи(молодое пространство плюс постоянное пространство).
-XX:+Отключить ExplicitGCЭто заставит JVM игнорировать любые вызовы метода System.gc() приложением.
-XX:+UseGCOverheadLimitЭто политика использования, применяемая для ограничения времени, затрачиваемого на сборку мусора, прежде чем будет выдана ошибка OutOfMemory.
-XX:GCTimeLimit=95Это ограничивает долю времени, потраченного на сборку мусора, прежде чем будет выдана ошибка OutOfMemory. Используется с GCHeapFreeLimit.
-XX:GCHeapFreeLimit=5Это устанавливает минимальный процент свободного места после полной сборки мусора, прежде чем будет выдана ошибка OutOfMemory. Это используется с GCTimeLimit.
-XX:НачальныйРазмерКучи=3gУстанавливает начальный размер кучи(молодое пространство плюс постоянное пространство).
-XX:MaxHeapSize=3gУстанавливает максимальный размер кучи(молодое пространство плюс постоянное пространство).
-XX:НовыйРазмер=128мУстанавливает начальный размер молодого пространства.
-XX:MaxNewSize=128mУстанавливает максимальный размер молодого пространства.
-XX:SurvivorRatio=15Устанавливает размер пространства для одного выжившего как часть размера пространства Эдема.
-XX:РазмерРазрешенного=512мУстанавливает начальный размер постоянного пространства.
-XX:MaxPermSize=512мУстанавливает максимальный размер постоянного пространства.
-Xss512kУстанавливает размер области стека, выделенной каждому потоку, в байтах.

Флаги регистрации GC

ФлагОписание
-verbose:gc или -XX:+PrintGCЭто выведет на экран основную информацию о сборе мусора.
-XX:+ПечатьGCПодробностиЭто позволит распечатать более подробную информацию о вывозе мусора.
-XX:+PrintGCTimeStampsВы можете распечатать временные метки для каждого события сборки мусора. Секунды являются последовательными и начинаются с момента запуска JVM.
-XX:+ПечатьGCDateStampsВы можете распечатать отметки о дате каждого мероприятия по вывозу мусора.
-Xloggc:Используя это, вы можете перенаправить вывод сборки мусора в файл вместо консоли.
-XX:+Печать\Распределение по времениВы можете распечатать подробную информацию о молодом пространстве после каждого цикла сбора.
-XX:+ПечатьTLABЭтот флаг можно использовать для печати статистики распределения TLAB.
-XX:+PrintReferenceGCИспользуя этот флаг, вы можете распечатать время обработки эталона(то есть слабой, мягкой и т. д.) во время пауз «стоп-мир».
-XX:+HeapDump\OnOutOfMemoryErrorЭто создает файл дампа кучи в условиях нехватки памяти.

Подведем итоги

Итак, в этом уроке по сборке мусора Java мы узнали следующее:

  1. Жизненный цикл объекта делится на 3 фазы: создание объекта, использование объекта и уничтожение объекта.
  2. Как работают механизмы маркировки-очистки, маркировки-очистки-уплотнения и маркировки-копирования.
  3. Различные однопоточные и параллельные алгоритмы сборки мусора.
  4. До Java 8 параллельный сборщик мусора был алгоритмом по умолчанию.
  5. Начиная с Java 9, G1 установлен в качестве алгоритма сборки мусора по умолчанию.
  6. А также различные флаги для управления поведением алгоритма сборки мусора и регистрации полезной информации для любого приложения.
Прокрутить вверх