Какой сборщик мусора разбивает память на множество регионов одинакового размера
В Java 8 сборщиком мусора по умолчанию был Parallel GC, в Java 9 ему на смену пришёл G1 Garbage Collector, который обеспечивает минимальное время stop-the-world (время когда сборщик мусора останавливает работу приложения).
Сборщик мусора Garbage-First (G1) нацелен на многопроцессорные системы с большим количеством памяти. Он старается с высокой точностью достичь заданной цели по времени остановки выполнения основной программы, но при этом добиться высокой пропускной способности при небольшой потребности в настройке. G1 нацелен на баланс между задержками и пропускной способностью.
В Java 9 сборщик мусора G1 используется по умолчанию, но вы также можете явно включить с помощью опции -XX:+UseG1GC.
Так же как и остальные сборщики мусора G1 разбивает кучу на молодое и старое поколения. Сборка мусора происходит по большей части в молодом поколении, чтобы увеличить пропускную способность сборки, сборка в старом поколении происходит гораздо реже.
Некоторые операции всегда выполняется в stop-the-world, чтобы увеличить пропускную способность, но некоторые операции, которые занимают большое время, например работающие со всей кучей, осуществляются параллельно с работой основного приложения. Для уменьшения времени stop-the-world сборщик мусора G1 осуществляет сборку инкрементно и параллельно с работой основного приложения. Сборщик мусора G1 собирает информацию о предыдущих сборках мусора, чтобы добиться более точного соблюдения времени stop-the-world. Например, он собирает мусор в первую очередь в тех местах, которые заполнены больше всего.
G1 переносит живые объекты из выбранных областей памяти в новые области, тем самым упаковывая их рядом для более эффективного использования памяти. После переноса выживших объектов память, которая была занята ими до этого процесса используется для новых объектов приложения.
Garbage-First collector — это НЕ сборщик мусора реального времени. Он пытается достичь заданного времени по stop-the-world, но это время не выдерживается для конкретной одной остановки приложения.
Сборщик G1 разбивает кучу на регионы одинакового размера. Каждый регион может быть либо свободен, либо содержать объекты из молодого поколения, либо содержать объекты из старого поколения. Регионов достаточно много, может быть множество регионов с молодым поколением и множество регионов со старым поколением. Регионы с молодым поколением делятся на Eden и Survivor. Они осуществляют точно такую же функцию, как и в сборщике мусора Serial GC.
Приложение всегда создаёт объекты в молодом поколении, в Eden, кроме очень больших объектов, которые сразу создаются в старом поколении.
Паузы для сборки мусора G1 очищают место в молодых поколениях полностью, и в дополнение могут очищать место в некотором количестве старых поколоениях во время любой из пауз. Во время паузы G1 копирует объекты из выбранных регионов в другие регионы в куче. Регион, куда происходит копирование, зависит от исходного региона: молодое поколение целиком копируется в survivor и старые регионы, а объекты из старых регионов в другие старые регионы.
Concurrent collector (конкурентный сборщик)
Concurrent collector выполняет сборку мусора вместе с работой потоков вашего приложения. Эффективен для приложений, обрабатывающих средние и большие наборы данных и требующих малого времени отклика.
Для молодого и старого поколений можно использовать разные, но совместимые, алгоритмы GC. Например, нельзя использовать Parallel Scavenge для молодого поколения одновременно с Concurrent Mark Sweep для старого, так как Parallel Scavenge не обеспечивает синхронизацию, которая требуется в CMS.
После Java 8 в сборке мусора произошло много изменений, о которых я расскажу в других статьях.
Для работы любого приложения требуется память. Однако память компьютера ограничена. Поэтому важно ее очищать от старых неиспользуемых данных, чтобы освободить место для новых.
Кто занимается этой очисткой? Как и когда очищается память? Как выглядит структура памяти? Давайте разберем с этим подробнее.
Настройка
Так как подходы к организации памяти у CMS аналогичны используемым в Serial / Parallel GC, для него применимы те же опции определения размеров регионов кучи, а также опции автоматической подстройки под требуемые параметры производительности.
Обычно CMS, основываясь на собираемой статистике о поведении приложения, сам определяет, когда ему выполнять старшую сборку, но у него также есть порог наполненности региона Tenured, при достижении которого должна обязательно быть инициирована старшая сборка. Этот порог можно задать с помощью опции , значение указывается в процентах. Значение -1 (иногда устанавливается по умолчанию) указывает на отключение сборки по такому условию.
Parallel collector (параллельный сборщик)
В отличие от последовательного сборщика, использующего один поток, параллельный использует несколько потоков. Эффективен при достаточном объеме памяти и количестве процессорных ядер.
Типы сборщиков мусора
Сборщик мусора
Описание
Преимущества
Когда использовать
Флаги для включения
Использует один поток.
Эффективный, т.к. нет накладных расходов на взаимодействие потоков.
Работа с небольшими наборами данных.
Использует несколько потоков.
Многопоточность ускоряет сборку мусора.
В приоритете пиковая производительность.
Допустимы паузы при GC в одну секунду и более.
Работа со средними и большими наборами данных.
Для приложений, работающих на многопроцессорном или многопоточном оборудовании.
Выполняет некоторую тяжелую работу параллельно с работой приложения.
Может использоваться как на небольших системах, так и на больших с большим количеством процессоров и большим количеством памяти.
Когда время отклика важнее пропускной способности.
Паузы GC должны быть меньше одной секунды.
Выполняет всю тяжелую работу параллельно с работой приложения.
В приоритете время отклика.
Сборщики мусора в Java
Принципы работы
Мы уже встречали слова Mark и Sweep при рассмотрении последовательного и параллельного сборщиков (если вы не встречали, то сейчас как раз самое время это сделать). Они обозначали два шага в процессе сборки мусора в старшем поколении: пометку выживших объектов и удаление мертвых объектов. Сборщик CMS получил свое название благодаря тому, что выполняет указанные шаги параллельно с работой основной программы.
При этом CMS GC использует ту же самую организацию памяти, что и уже рассмотренные Serial / Parallel GC: регионы Eden + Survivor 0 + Survivor 1 + Tenured и такие же принципы малой сборки мусора. Отличия начинаются только тогда, когда дело доходит до полной сборки. В случае CMS ее называют старшей (major) сборкой, а не полной, так как она не затрагивает объекты младшего поколения. В результате, малая и старшая сборки здесь всегда разделены. Одним из побочных эффектов такого разделения является то, что все объекты младшего поколения (даже потенциально мертвые) могут играть роль корней при определении статуса объектов в старшем поколении.
Важным отличием сборщика CMS от рассмотренных ранее является также то, что он не дожидается заполнения Tenured для того, чтобы начать старшую сборку. Вместо этого он трудится в фоновом режиме постоянно, пытаясь поддерживать Tenured в компактном состоянии.
Давайте рассмотрим, что из себя представляет старшая сборка мусора при использовании CMS GC.
Начинается она с остановки основных потоков приложения и пометки всех объектов, напрямую доступных из корней. После этого приложение возобновляет свою работу, а сборщик параллельно с ним производит поиск всех живых объектов, доступных по ссылкам из тех самых помеченных корневых объектов (эту часть он делает в одном или в нескольких потоках).
Естественно, за время такого поиска ситуация в куче может поменяться, и не вся информация, собранная во время поиска живых объектов, оказывается актуальной. Поэтому сборщик еще раз приостанавливает работу приложения и просматривает кучу для поиска живых объектов, ускользнувших от него за время первого прохода. При этом допускается, что в живые будут записаны объекты, которые на время окончания составления списка таковыми уже не являются. Эти объекты называются плавающим мусором (floating garbage), они будут удалены в процессе следующей сборки.
После того, как живые объекты помечены, работа основных потоков приложения возобновляется, а сборщик производит очистку памяти от мертвых объектов в нескольких параллельных потоках. При этом следует иметь в виду, что после очистки не производится упаковка объектов в старшем поколении, так как делать это при работающем приложении весьма затруднительно.
Сборщик CMS достаточно интеллектуальный. Например, он старается разносить во времени малые и старшие сборки мусора, чтобы они совместно не создавали продолжительных пауз в работе приложения (дополнительные подробности об этом разнесении в комментариях). Для этого он ведет статистику по прошедшим сборкам и исходя из нее планирует последующие.
Отдельно следует рассмотреть ситуацию, когда сборщик не успевает очистить Tenured до того момента, как память полностью заканчивается. В этом случае работа приложения останавливается, и вся сборка производится в последовательном режиме. Такая ситуация называется сбоем конкурентного режима (concurrent mode failure). Сборщик сообщает нам об этих сбоях при включенных опциях -verbose:gc или -Xloggc:filename.
У CMS есть один интересный режим работы, называемый Incremental Mode, или i-cms, который заставляет его временно останавливаться при выполнении работ параллельно с основным приложением, чтобы на короткие периоды высвобождать ресурсы процессора (что-то вроде АБС у автомобиля). Это может быть полезным на машинах с малым количеством ядер. Но данный режим уже помечен как не рекомендуемый к применению и может быть отключен в будущих релизах, поэтому подробно его разбирать не будем.
Немного о внутренностях G1 collector
The Initial Heap Occupancy Percent (IHOP) — это порог, после которого запускается начальная пометка. По умолчанию он определяется автоматически на основе времени, который требуется на пометку.
Для пометки G1 использует алгоритм, называемый Snapshot-At-The-Beginning (SATB). Он делает виртуальный снапшот кучи в момент начала этапа начальной пометки, то есть считает, что все объекты, которые были живы на момент начала пометки, живы до самого конца процесса начальной пометки (Initial Mark).
Очень большие обхекты располагаются в нескольких последовательных регионах старого поколоения.
В этой статье поговорим о сборке мусора (Garbage Collection) в java.
Память в JVM делится на три части:
Young generation (молодое поколение).
Old generation (старое поколение).
Metaspace (Perm Gen).
Структура памяти Java
Память в Java состоит из следующих областей:
Структура памяти Java
Native Memory — вся доступная системная память.
Heap (куча) — часть native memory, выделенная для кучи. Здесь JVM хранит объекты. Это общее пространство для всех потоков приложения. Размер этой области памяти настраивается с помощью параметра -Xms (минимальный размер) и -Xmx (максимальный размер).
Stack (стек) — используется для хранения локальных переменных и стека вызовов метода. Для каждого потока выделяется свой стек.
Metaspace (метаданные) — в этой памяти хранятся метаданные классов и статические переменные. Это пространство также является общими для всех. Так как metaspace является частью native memory, то его размер зависит от платформы. Верхний предел объема памяти, используемой для metaspace, можно настроить с помощью флага MaxMetaspaceSize.
PermGen (Permanent Generation, постоянное поколение) присутствовало до Java 7. Начиная с Java 8 ему на смену пришла область Metaspace.
CodeCache (кэш кода) — JIT-компилятор компилирует часто исполняемый код, преобразует его в нативный машинный код и кеширует для более быстрого выполнения. Это тоже часть native memory.
Цикл сборки мусора G1
- Сборка только в молодом поколении (Young-only phase): Этот этап перетаскивает объекты из регионов с молодым поколением в регионы со старым поколением. Переход между сборкой только в молодом поколении и этапом освобождения памяти происходит в момент достижения определённого порога заполнения кучи, в который G1 стартует процесс пометки только молодого поколения:
— Начальная пометка (Initial Mark): Помечаются достижимые (живые) объекты в регионах со старым поколением. Этот процесс выполняется БЕЗ stop-the-world. Во время этого процесса также могут происходить сборки только в молодом поколении. После процесса начальной пометки выполняются два этапа с полной остановкой приложения (stop-the-world)..
— Повторная пометка (Remark): Завершает пометку, осуществляет выгрузку классов и глобальную обработку ссылок. Между повторной пометкой и очисткой сборщик мусора вычисляет общую информацию о живых объектах конкурентно (параллельно с работой приложения), которые будут освобождены, эта информация будет использоваться на этапе чистки.
— Чистка (Cleanup): очищает полностью пустые регионы, определяет нужен ли этап освобождения места. - Освобождение места (Space-reclamation phase): Этот этап состоит из из сборки мусора, которая в дополнение к молодым регионам также перемещает живые объекты из множества старых регионов. Этап заканчивается, когда G1 решает, что дальнейшее перемещение ркегионыов не приведёт к освобожждению достаточного места, чтобы этим заниматься.
Ситуации STW
- Процессы переноса объектов между поколениями. Для минимизации таких пауз G1 использует несколько потоков.
- Короткая фаза начальной пометки корней в рамках цикла пометки.
- Более длинная пауза в конце фазы remark и в начале фазы cleanup цикла пометки.
Алгоритмы сборки мусора
В JVM есть несколько сборщиков мусора с разными алгоритмами для молодого и старого поколения. Есть три типа алгоритмов.
Настройка
Так как основной целью сборщика G1 является минимизация пауз в работе основного приложения, то и главной опцией при его настройке можно считать уже встречавшуюся нам , задающую приемлемое для нас максимальное время разовой сборки мусора. Даже если вы не собираетесь задавать это свойство, хотя бы проверьте его значение по умолчанию. Хотя в документации Oracle и говориться, что по умолчанию время сборки не ограничено, но по факту это не всегда так.
Опции и задают количество потоков, которые будут использоваться для сборки мусора и для выполнения цикла пометок соответственно.
Если вас не устраивает автоматический выбор размера региона, вы можете задать его вручную с помощью опции . Значение должно быть степенью двойки, если мерить в мегабайтах. Например, .
При желании можно изменить порог заполненности кучи, при достижении которого инициируется выполнение цикла пометок и переход в режим смешанных сборок. Это делается опцией , принимающей значение в процентах. По умолчанию, этот порог равен 45%.
Если же вы решите залезть в дебри настроек G1 по-глубже, то можете включить дополнительные функции опциями и и поиграть с экспериментальными настройками.
Сборка мусора: введение
Что такое "мусор"? Мусором считается объект, который больше не может быть достигнут по ссылке из какого-либо объекта. Поскольку такие объекты больше не используются в приложении, то их можно удалить из памяти.
Например, на диаграмме ниже объект fruit2 может быть удален из памяти, поскольку на него нет ссылок.
Мусор
Что такое сборка мусора? Сборка мусора — это процесс автоматического управления памятью. Освобождение памяти (путем очистки мусора) выполняется автоматически специальным компонентом JVM — сборщиком мусора (Garbage Collector, GC). Нам, как программистам, нет необходимости вмешиваться в процесс сборки мусора.
Немного о внутренностях G1 collector
The Initial Heap Occupancy Percent (IHOP) — это порог, после которого запускается начальная пометка. По умолчанию он определяется автоматически на основе времени, который требуется на пометку.
Для пометки G1 использует алгоритм, называемый Snapshot-At-The-Beginning (SATB). Он делает виртуальный снапшот кучи в момент начала этапа начальной пометки, то есть считает, что все объекты, которые были живы на момент начала пометки, живы до самого конца процесса начальной пометки (Initial Mark).
Очень большие объекты располагаются в нескольких последовательных регионах старого поколоения.
Инструменты мониторинга GC
Что мониторить?
Частота запуска сборки мусора. Так как GC вызывает "stop the world", поэтому чем время сборки мусора меньше, тем лучше.
Длительность одного цикла сборки мусора.
Как мониторить сборщик мусора?
Для мониторинга можно использовать следующие инструменты:
Для включения логирования событий сборщика мусора добавьте следующие параметры JVM:
Сегодня мы продолжаем цикл статей о сборщиках мусора, поставляемых с виртуальной машиной Oracle Java HotSpot VM. Мы уже изучили немного теории и рассмотрели, каким образом с кучей расправляются два базовых сборщика — Serial GC и Parallel GC. А в этой статье речь пойдет о сборщиках CMS GC и G1 GC, первостепенной задачей которых является минимизация пауз при наведении порядка в памяти приложений, оперирующих средними и большими объемами данных, то есть по большей части в памяти серверных приложений.
Два этих сборщика объединяют общим названием «mostly concurrent collectors», то есть «по большей части конкурентные сборщики». Это связано с тем, что часть своей работы они выполняют параллельно с основными потоками приложения, то есть в какие-то моменты конкурируют с ними за ресурсы процессора. Конечно, это не проходит бесследно, и в итоге они разменивают улучшение в части пауз на ухудшение в части пропускной способности. Хотя делают это по-разному. Давайте посмотрим, как.
Использование CMS GC включается опцией -XX:+UseConcMarkSweepGC.
Young Generation (молодое поколение)
Как следует из названия, Young Generation — это область памяти для новых, вновь создаваемых объектов.
Когда область Young Generation заполняется, то запускается минорная сборка мусора (Minor GC).
При Minor GC "мертвые" объекты удаляются из Young Generation.
Чем больше "мертвых" объектов в Young Generation, тем быстрее выполняется Minor GC.
При Minor GC происходит "остановка мира" (stop the world) — все потоки в приложении останавливаются.
Давайте подробнее разберемся со структурой Young generation.
Young generation разделен на три части: Eden, S0, S1.
Все новые объекты размещаются в Eden Space.
При заполнении Еden space происходит minor GC: все "живые" объекты перемещаются в одно из пространств уцелевших объектов (survivor space): S0 или S1. Допустим, в нашем случае, все объекты будут перемещены в S0.
Для дальнейшего эксперимента я написал и запустил программу, которая создает короткоживущие объекты.
Давайте посмотрим распределение памяти в Visual GC (плагин для VisualVM).
Как видно, в S0 совсем мало объектов, и как только Еden Space заполняется, все объекты, на которые есть ссылки, перемещаются в S1.
Ситуации STW
- Малая сборка мусора. Эта пауза ничем не отличается от аналогичной паузы в Parallel GC.
- Начальная фаза поиска живых объектов при старшей сборке (так называемая initial mark pause). Эта пауза обычно очень короткая.
- Фаза дополнения набора живых объектов при старшей сборке (известная также как remark pause). Она обычно длиннее начальной фазы поиска.
Сборка мусора: флаги
В этом разделе приведены некоторые важные флаги, которые можно использовать для настройки процесса сборки мусора.
Флаг
Описание
Первоначальный размер кучи
Максимальный размер куча
Отношение размера Old Generation к Young Generation
Отношение размера Eden к Survivor
Возраст объекта, когда объект перемещается из области Survivor в область Old Generation
Зачем используется два survivor space?
Вы наверняка задумались над тем, почему используется два survivor space? Для борьбы с фрагментацией памяти. При каждом копировании объектов из eden в survivor, вы получаете пустой eden и один пустой survivor.
Цикл сборки мусора G1
- Сборка только в молодом поколении (Young-only phase): Этот этап перетаскивает объекты из регионов с молодым поколением в регионы со старым поколением. Переход между сборкой только в молодом поколении и этапом освобождения памяти происходит в момент достижения определённого порога заполнения кучи, в который G1 стартует процесс пометки только молодого поколения:- Начальная пометка (Initial Mark): Помечаются достижимые (живые) объекты в регионах со старым поколением. Этот процесс выполняется БЕЗ stop-the-world. Во время этого процесса также могут происходить сборки только в молодом поколении. После процесса начальной пометки выполняются два этапа с полной остановкой приложения (stop-the-world)..
— Повторная пометка (Remark): Завершает пометку, осуществляет выгрузку классов и глобальную обработку ссылок. Между повторной пометкой и очисткой сборщик мусора вычисляет общую информацию о живых объектах конкурентно (параллельно с работой приложения), которые будут освобождены, эта информация будет использоваться на этапе чистки.
— Чистка (Cleanup): очищает полностью пустые регионы, определяет нужен ли этап освобождения места.
2. Освобождение места (Space-reclamation phase): Этот этап состоит из из сборки мусора, которая в дополнение к молодым регионам также перемещает живые объекты из множества старых регионов. Этап заканчивается, когда G1 решает, что дальнейшее перемещение ркегионыов не приведёт к освобожждению достаточного места, чтобы этим заниматься.
Принципы работы
Первое, что бросается в глаза при рассмотрении G1 — это изменение подхода к организации кучи. Здесь память разбивается на множество регионов одинакового размера. Размер этих регионов зависит от общего размера кучи и по умолчанию выбирается так, чтобы их было не больше 2048, обычно получается от 1 до 32 МБ. Исключение составляют только так называемые громадные (humongous) регионы, которые создаются объединением обычных регионов для размещения очень больших объектов.
Разделение регионов на Eden, Survivor и Tenured в данном случае логическое, регионы одного поколения не обязаны идти подряд и даже могут менять свою принадлежность к тому или иному поколению. Пример разделения кучи на регионы может выглядеть следующим образом (количество регионов сильно приуменьшено):
Малые сборки выполняются периодически для очистки младшего поколения и переноса объектов в регионы Survivor, либо их повышения до старшего поколения с переносом в Tenured. Над переносом объектов трудятся несколько потоков, и на время этого процесса работа основного приложения останавливается. Это уже знакомый нам подход из рассмотренных ранее сборщиков, но отличие состоит в том, что очистка выполняется не на всем поколении, а только на части регионов, которые сборщик сможет очистить не превышая желаемого времени. При этом он выбирает для очистки те регионы, в которых, по его мнению, скопилось наибольшее количество мусора и очистка которых принесет наибольший результат. Отсюда как раз название Garbage First — мусор в первую очередь.
- Initial mark. Пометка корней (с остановкой основного приложения) с использованием информации, полученной из малых сборок.
- Concurrent marking. Пометка всех живых объектов в куче в нескольких потоках, параллельно с работой основного приложения.
- Remark. Дополнительный поиск не учтенных ранее живых объектов (с остановкой основного приложения).
- Cleanup. Очистка вспомогательных структур учета ссылок на объекты и поиск пустых регионов, которые уже можно использовать для размещения новых объектов. Первая часть этого шага выполняется при остановленном основном приложении.
После окончания цикла пометки G1 переключается на выполнение смешанных сборок. Это значит, что при каждой сборке к набору регионов младшего поколения, подлежащих очистке, добавляется некоторое количество регионов старшего поколения. Количество таких сборок и количество очищаемых регионов старшего поколения выбирается исходя из имеющейся у сборщика статистики о предыдущих сборках таким образом, чтобы не выходить за требуемое время сборки. Как только сборщик очистил достаточно памяти, он переключается обратно в режим малых сборок.
Очередной цикл пометки и, как следствие, очередные смешанные сборки будут запущены тогда, когда заполненность кучи превысит определенный порог.
Смешанная сборка мусора в приведенном выше примере кучи может пройти вот так:
Может оказаться так, что в процессе очистки памяти в куче не остается свободных регионов, в которые можно было бы копировать выжившие объекты. Это приводит к возникновению ситуации allocation (evacuation) failure, подобие которой мы видели в CMS. В таком случае сборщик выполняет полную сборку мусора по всей куче при остановленных основных потоках приложения.
Опираясь на уже упомянутую статистику о предыдущих сборках, G1 может менять количество регионов, закрепленных за определенным поколением, для оптимизации будущих сборок.
Serial collector (последовательный сборщик)
Для сборки мусора используется один поток. Подходит для простых приложений с однопроцессорными машинами.
Достоинства и недостатки
Достоинством данного сборщика по сравнению с рассмотренными ранее Serial / Parallel GC является его ориентированность на минимизацию времен простоя, что является критическим фактором для многих приложений. Но для выполнения этой задачи приходится жертвовать ресурсами процессора и зачастую общей пропускной способностью.
Вспомним еще, что данный сборщик не уплотняет объекты в старшем поколении, что приводит к фрагментации Tenured. Этот факт в совокупности с наличием плавающего мусора приводит к необходимости выделять приложению (конкретно — старшему поколению) больше памяти, чем потребовалось бы для других сборщиков (Oracle советует на 20% больше).
Ну и долгие паузы при потенциально возможных сбоях конкурентного режима могут стать неприятным сюрпризом. Хотя они не частые, и при наличии достаточного объема памяти CMS’у удается их полностью избегать.
Тем не менее, такой сборщик может подойти приложениям, использующим большой объем долгоживущих данных. В этом случае некоторые его недостатки нивелируются. Но в любом случае, не стоит принимать решение о его использовании пока вы не познакомились с еще одним сборщиком в обойме Java HotSpot VM.
Вот мы и добрались до последнего и наверняка самого интересного для многих сборщика мусора — G1 (что является сокращением от Garbage First). Интересен он прежде всего тем, что не является явным продолжением линейки Serial / Parallel / CMS, добавляющим параллельность еще в какую-нибудь фазу сборки мусора, а использует уже существенно отличающийся подход к задаче очистки памяти.
G1 — самый молодой в составе сборщиков мусора виртуальной машины HotSpot. Он изначально позиционировался как сборщик для приложений с большими кучами (от 4 ГБ и выше), для которых важно сохранять время отклика небольшим и предсказуемым, пусть даже за счет уменьшения пропускной способности. На этом поле он конкурировал с CMS GC, хотя изначально и не так успешно, как хотелось бы. Но постепенно он исправлялся, улучшался, стабилизировался и, наконец, достиг такого уровня, что Oracle говорит о нем как о долгосрочной замене CMS, а в Open JDK даже серьезно рассматривают его на роль сборщика по умолчанию для серверных конфигураций в 9-й версии.
Это все явно стоит того, чтобы разобраться с его устройством. Не будем же откладывать.
G1 включается опцией Java -XX:+UseG1GC.
Поколения объектов
Что такое поколения объектов?
Для оптимизации сборки мусора память кучи дополнительно разделена на четыре области. В эти области объекты помещаются в зависимости от их возраста (как долго они используются в приложении).
Young Generation (молодое поколение). Здесь создаются новые объекты. Область young generation разделена на три части раздела: Eden (Эдем), S0 и S1 (Survivor Space — область для выживших).
Old Generation (старое поколение). Здесь хранятся давно живущие объекты.
Что такое Stop the World?
Когда запускается этап mark, работа приложения останавливается. После завершения mark приложение возобновляет свою работу. Любая сборка мусора — это "Stop the World".
Что такое гипотеза о поколениях?
Как уже упоминалось ранее, для оптимизации этапов mark и sweep используются поколения. Гипотеза о поколениях говорит о следующем:
Большинство объектов живут недолго.
Если объект выживает, то он, скорее всего, будет жить вечно.
Этапы mark и sweep занимают меньше времени при большом количестве мусора. То есть маркировка будет происходить быстрее, если анализируемая область небольшая и в ней много мертвых объектов.
Таким образом, алгоритм сборки мусора, использующий поколения, выглядит следующим образом:
Новые объекты создаются в области Eden. Области Survivor (S0, S1) на данный момент пустые.
Когда область Eden заполняется, происходит минорная сборка мусора (Minor GC). Minor GC — это процесс, при котором операции mark и sweep выполняются для young generation (молодого поколения).
После Minor GC живые объекты перемещаются в одну из областей Survivor (например, S0). Мертвые объекты полностью удаляются.
По мере работы приложения пространство Eden заполняется новыми объектами. При очередном Minor GC области young generation и S0 очищаются. На этот раз выжившие объекты перемещаются в область S1, и их возраст увеличивается (отметка о том, что они пережили сборку мусора).
При следующем Minor GC процесс повторяется. Однако на этот раз области Survivor меняются местами. Живые объекты перемещаются в S0 и у них увеличивается возраст. Области Eden и S1 очищаются.
Объекты между областями Survivor копируются определенное количество раз (пока не переживут определенное количество Minor GC) или пока там достаточно места. Затем эти объекты копируются в область Old.
Major GC. При Major GC этапы mark и sweep выполняются для Old Generation. Major GC работает медленнее по сравнению с Minor GC, поскольку старое поколение в основном состоит из живых объектов.
Преимущества использования поколений
Minor GC происходит в меньшей части кучи (~ 2/3 от кучи). Этап маркировки эффективен, потому что область небольшая и состоит в основном из мертвых объектов.
Недостатки использования поколений
В каждый момент времени одно из пространств Survivor (S0 или S1) пустое и не используется.
Достоинства и недостатки
В целом считается, что сборщик G1 более аккуратно предсказывает размеры пауз, чем CMS, и лучше распределяет сборки во времени, чтобы не допустить длительных остановок приложения, особенно при больших размерах кучи. При этом он лишен и некоторых других недостатков CMS, например, он не фрагментирует память.
Расплатой за достоинства G1 являются ресурсы процессора, которые он использует для выполнения достаточно большой части своей работы параллельно с основной программой. В результате страдает пропускная способность приложения. Целевым значением пропускной способности по умолчанию для G1 является 90%. Для Parallel GC, например, это значение равно 99%. Это, конечно, не значит, что пропускная способность с G1 всегда будет почти на 10% меньше, но данную особенность следует всегда иметь в виду.
Вот мы и разобрали алгоритмы работы всех четырех сборщиков мусора в виртуальной машине HotSpot. В следующей статье попробуем разобраться, каким образом эти знания можно применять для оптимизации работы приложений.
В Java 8 сборщиком мусора по умолчанию был Parallel GC, в Java 9 это изменилось. Ему на смену пришёл G1 Garbage Collector, который обеспечивает минимальное время stop-the-world (время когда сборщик мусора останавливает работу приложения).
Сборщик мусора Garbage-First (G1) нацелен на многопроцессорные системы с большим количеством памяти. Он старается с высокой точностью достичь заданной цели по времени остановки выполнения основной программы, но при этом добиться высокой пропускной способности при небольшой потребности в настройке. G1 нацелен на баланс между задержками и пропускной способностью.
В Java 9 сборщик мусора G1 используется по умолчанию, но вы также можете явно включить с помощью опции -XX:+UseG1GC .
Так же как и остальные сборщики мусора G1 разбивает кучу на молодое и старое поколения. Сборка мусора происходит по большей части в молодом поколении, чтобы увеличить пропускную способность сборки, сборка в старом поколении происходит гораздо реже.
Некоторые операции всегда выполняется в stop-the-world, чтобы увеличить пропускную способность, но некоторые операции, которые занимают большое время, например работающие со всей кучей, осуществляются параллельно с работой основного приложения. Для уменьшения времени stop-the-world сборщик мусора G1 осуществляет сборку инкрементно и параллельно с работой основного приложения. Сборщик мусора G1 собирает информацию о предыдущих сборках мусора, чтобы добиться более точного соблюдения времени stop-the-world. Например, он собирает мусор в первую очередь в тех местах, которые заполнены больше всего.
G1 переносит живые объекты из выбранных областей памяти в новые области, тем самым упаковывая их рядом для более эффективного использования памяти. После переноса выживших объектов память, которая была занята ими до этого процесса используется для новых объектов приложения.
Garbage-First collector — это НЕ сборщик мусора реального времени. Он пытается достичь заданного времени по stop-the-world, но это время не выдерживается для конкретной одной остановки приложения.
Сборщик G1 разбивает кучу на регионы одинакового размера. Каждый регион может быть либо свободен, либо содержать объекты из молодого поколения, либо содержать объекты из старого поколения. Регионов достаточно много, может быть множество регионов с молодым поколением и множество регионов со старым поколением. Регионы с молодым поколением делятся на Eden и Survivor. Они осуществляют точно такую же функцию, как и в сборщике мусора Serial GC.
Приложение всегда создаёт объекты в молодом поколении, в Eden, кроме очень больших объектов, которые сразу создаются в старом поколении.
Паузы для сборки мусора G1 очищают место в молодых поколениях полностью, и в дополнение могут очищать место в некотором количестве старых поколоениях во время любой из пауз. Во время паузы G1 копирует объекты из выбранных регионов в другие регионы в куче. Регион, куда происходит копирование, зависит от исходного региона: молодое поколение целиком копируется в survivor и старые регионы, а объекты из старых регионов в другие старые регионы.
Old generation (старое поколение)
В Old generation располагаются долгоживущие объекты.
Как правило, эта область больше, чем область для young generation.
При заполнении (или достижении заданного порога) происходит Major GC.
Обычно Major GC выполняются медленнее и реже, чем minor GC.
Как с помощью этой информации можно оптимизировать использование памяти?
Все зависит от типа приложения.
Если у вас много временных объектов, то будет много Minor GC. В этом случае можно использовать параметр XX:NewRatio=1, чтобы распределить 50% на young generation и 50% на old generation.
По умолчанию NewRatio=2, т.е. young generation составляет 1/3 всей кучи (heap).
При множестве долгоживущих объектов можно сделать область старого поколения больше, увеличив NewRatio.
Сборка мусора: процесс
Для сборки мусора используется алгоритм пометок (Mark & Sweep). Этот алгоритм состоит из трех этапов:
Mark (маркировка). На первом этапе GC сканирует все объекты и помечает живые (объекты, которые все еще используются). На этом шаге выполнение программы приостанавливается. Поэтому этот шаг также называется "Stop the World" .
Sweep (очистка). На этом шаге освобождается память, занятая объектами, не отмеченными на предыдущем шаге.
Compact (уплотнение). Объекты, пережившие очистку, перемещаются в единый непрерывный блок памяти. Это уменьшает фрагментацию кучи и позволяет проще и быстрее размещать новые объекты.
Гиганты
- Он никогда не перемещается между регионами.
- Он может удаляться в рамках цикла пометки или полной сборки мусора.
- В регион, занятый громадным объектом, больше никого не подселяют, даже если в нем остается свободное место.
В продолжении данного цикла статей мы посмотрим, как с этим можно бороться.
Читайте также: