Сборка мусора(GC) была одной из замечательных функций Java, обеспечивших ее популярность. Сборка мусора — это механизм, используемый в Java для освобождения неиспользуемой памяти. По сути, он отслеживает все объекты, которые все еще используются, и помечает остальные как мусор. Сборка мусора Java считается автоматической схемой управления памятью, поскольку программистам не нужно обозначать объекты как готовые к освобождению. Сборка мусора выполняется в потоках с низким приоритетом.
В этом руководстве мы рассмотрим различные концепции, связанные с выделением/освобождением памяти, алгоритмы, работающие в фоновом режиме, и доступные вам возможности настройки этого поведения.
Жизненный цикл объекта Java можно разделить на 3 этапа:
Создание объекта
Чтобы создать объект, обычно мы используем ключевое слово new. Например:
Object obj = new Object();
При создании объекта выделяется определенный объем памяти для хранения этого объекта. Объем выделяемой памяти может различаться в зависимости от архитектуры и JVM.
Объект в использовании
До тех пор, пока объект не будет использоваться другими объектами приложения(другие живые объекты имеют ссылки, указывающие на него). Во время использования объект находится в памяти и может содержать ссылки на другие объекты.
Уничтожение объектов
Система сбора мусора отслеживает объекты и, по возможности, подсчитывает количество ссылок на каждый объект. Когда нет ссылок на объект, нет способа добраться до него с помощью текущего кода, поэтому имеет смысл освободить связанную с ним память.
Алгоритмы сбора мусора
Создание объектов выполняется кодом, который вы пишете; и фреймворками, которые вы используете для использования их предоставляемых функций. Как разработчик Java, мы не обязаны освобождать память или разыменовывать объекты. Это делается автоматически на уровне JVM сборщиком мусора. С момента появления Java было много обновлений алгоритмов, которые работают за сценой для освобождения памяти. Давайте посмотрим, как они работают?
Отметить и подмести
Это начальный и очень простой алгоритм, который выполняется в два этапа:
- Маркировка живых объектов – найдите все объекты, которые еще живы.
- Удаление недоступных объектов – избавьтесь от всего остального – предположительно мертвых и неиспользуемых объектов.
Для начала GC определяет некоторые конкретные объекты как корни сбора мусора. Например, локальные переменные и входные параметры текущих выполняемых методов, активные потоки, статические поля загруженных классов и ссылки JNI. Теперь GC обходит весь граф объектов в вашей памяти, начиная с этих корней и следуя ссылкам от корней к другим объектам. Каждый объект, который посещает GC, помечается как живой.
Потоки приложения должны быть остановлены для того, чтобы маркировка произошла, поскольку она не может реально пройти по графу, если он продолжает меняться. Это называется Stop The World pause.
Второй этап — избавление от неиспользуемых объектов для освобождения памяти. Это можно сделать разными способами, например
- Обычное удаление – Обычное удаление удаляет неиспользуемые объекты, чтобы освободить место и оставляет ссылаемые объекты и указатели. Распределитель памяти(вид хеш-таблицы) хранит ссылки на блоки свободного пространства, где может быть выделен новый объект.Его часто называют алгоритмом «марк-зачистка».
Обычное удаление – пометить и очистить
- Удаление со сжатием — удаление только неиспользуемых объектов неэффективно, поскольку блоки свободной памяти разбросаны по всей области хранения и вызывают ошибку OutOfMemoryError, если созданный объект достаточно большой и не находит достаточно большой блок памяти.Чтобы решить эту проблему, после удаления неиспользуемых объектов выполняется сжатие оставшихся ссылочных объектов. Здесь сжатие относится к процессу перемещения ссылочных объектов вместе. Это делает новое распределение памяти намного проще и быстрее.Его часто называют алгоритмом «маркировка-зачистка-компактность».
Удаление с уплотнением
- Удаление с копированием – очень похоже на подход маркировки и сжатия, поскольку они тоже перемещают все живые объекты. Важное отличие в том, что целью перемещения является другая область памяти.Его часто называют алгоритмом маркировки-копирования.
Удаление с копированием – 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 отслеживает количество данных в реальном времени, содержащихся в каждом регионе. Эта информация используется для определения регионов, содержащих больше всего мусора; поэтому они собираются в первую очередь. Вот почему это называется сбор мусора в первую очередь.
Как и другие алгоритмы, к сожалению, операция уплотнения происходит с использованием подхода 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 мы узнали следующее:
- Жизненный цикл объекта делится на 3 фазы: создание объекта, использование объекта и уничтожение объекта.
- Как работают механизмы маркировки-очистки, маркировки-очистки-уплотнения и маркировки-копирования.
- Различные однопоточные и параллельные алгоритмы сборки мусора.
- До Java 8 параллельный сборщик мусора был алгоритмом по умолчанию.
- Начиная с Java 9, G1 установлен в качестве алгоритма сборки мусора по умолчанию.
- А также различные флаги для управления поведением алгоритма сборки мусора и регистрации полезной информации для любого приложения.