Garbage Collector
Дорогу осилит идущийGarbage Collector (gc) — механизм JVM, отвечающий за очистку памяти от ненужных объектов (мусора). Именно он позволяет абстрагироваться от прямого управления памятью и работы с деструкторами, актуальных, например, в C++.
Мы упоминали об этом механизме в прошлом уроке, посвященном памяти JVM. Пришло время разобраться подробнее.
Важно понимать, что gc — абсолютно независимый от разработчика механизм (исключая возможность выбора реализации в настройках JVM). Максимум, что мы можем сделать в рамках кодовой базы — рекомендовать gc произвести сборку мусора, используя метод класса System: System.gc(). Однако вызов этого метода не дает гарантии, что Garbage Collector действительно будет запущен. В том числе поэтому в реальных задачах этот метод не используется.
Базовое знакомство с gc предлагаю осуществить на основе видео:
Стратегии сборки мусора
GC использует два подхода, чтобы определить, является объект мусорным:
- Подсчет количества ссылок на объект. Если на объект нет ссылок — он считается мусором. Про эту стратегию мы упоминали в предыдущих уроках. Ее недостаток в том, что два (или более) объекта могут ссылаться друг на друга (объект 1 хранится в поле объекта 2 и наоборот). Такая ситуация называется циклической зависимостью (circular dependency). Отсюда истекает актуальность второй стратегии;
- Невозможность получить доступ к объекту через корневую точку (root). Корневыми точками считаются локальные и статические переменные. Таким образом, если нет возможности добраться хотя бы до одного объекта, участвующего в циклической зависимости, все объекты в этой циклической зависимости будут признаны мусорными.
Основные понятия
В рамках знакомства с GC стоит ознакомиться со следующими терминами:
- Stop-the-world — время, на которое GC приостанавливает выполнение программы для сборки мусора. Сама по себе операция сборки мусора достаточно дорогая. В том числе потому что сборка мусора полностью останавливает работу программы. В ряде реализаций GC эта проблема решается, или, по крайней мере, сводится к минимуму;
- Потребляемые ресурсы – объем RAM (оперативная память) и процессорное время, потребляемое GC во время работы;
- Пропускная способность – отношение времени работы GC к общему времени работы программы.
Типы (реализации) GC
Существует несколько реализаций Garbage Collector, которые отличаются алгоритмами и/или подходами к очистке мусора. В большинстве случаев, для их понимания нужно достаточно хорошо понимать устройство памяти (точнее, кучи) в JVM.
Итак, реализации GC:
- Serial GC;
- Parallel GC;
- CMS (Concurrent Mark Sweep) GC;
- G1 (Garbage-First) GC;
- Epsilon GC.
Рассмотрим основные особенности каждого из них. Не страшно, если вы не запомните весь объем информации. Но советую, хотя бы с большего, уложить в голове информацию о Serial и Epsilon GC.
Serial GC:
- При создании объекты помещаются в Eden Space. Часть New (оно же Young) Generation;
- Когда он заполняется, запускается быстрая сборка мусора. На этот момент области Survivor Space — S0 и S1 — пусты;
- Объекты, пережившие быструю сборку мусора, переносятся в S0;
- При заполнении S0 (S1 пуст) запускается повторная сборка мусора;
- «Выжившие» объекты из S0 переносятся в S1. Также в него переносятся объекты, пережившие эту сборку в Eden Space (если такие были добавлены после первой сборки). По итогам этой операции S0 окажется пустым;
- Последующая сборка мусора будет аналогична п.5, только S1 и S0 поменяются местами;
- По итогу нескольких сборок пережившие их объекты перемещаются в Old Generation. Его очистка производится только по факту его заполнения (мы касались этого момента в предыдущем уроке).
Parallel GC отличается от Serial GC только тем, что алгоритм работает в многопоточном режиме.
CMS GC
Ключевая особенность этой реализации в том, что она пытается минимизировать время простоя, выполняя основной объем работ одновременно с потоками приложения. Как правило, этот сборщик минимизирует затраты на копирование объектов. По крайней мере, для Old Generation. В рамках Young Generation алгоритм не отличается от Parallel GC.
На мой взгляд, подробное описание работы CMS GC в рамках Old Generation сейчас несколько избыточно, по крайней мере, в этом уроке. Для тех, кого заинтересовала тема, предлагаю ознакомиться с официальной документацией. Информация о работе CMS GC (как и о других сборщиках, в т.ч. не освещенных в этой статье) там также присутствует: https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
G1
Отличается тем, что делит heap не на описанную выше (и в статье о памяти JVM) структуру (хотя результат с точки зрения логики будет похож), а на несколько областей равного размера, получая в итоге несколько областей Eden Space, несколько Survivor Space и несколько Old Generation. В итоге получается не более 2048 областей памяти.
Несколько потоков будут работать над очисткой Eden-областей, перенося выжившие объекты в Survivor-области (в этой части он похож на Serial GC). Важная особенность — очистке подвергаются не все Eden-области, а лишь те, которые признаются наиболее нуждающимися в этом.
В рамках сборок мусора приложение останавливается.
Epsilon GC
Эта реализация Garbage Collector добавилась в Java 11. Она характерна тем, что не занимается сборкой мусора. GC пометит мусорные объекты, но не будет тратить усилий на их удаление. Это может быть полезно для небольших приложений, в которых существует уверенность в том, что heap не будет переполнен (в силу небольшого количества объектов), соответственно, издержки на его очистку избыточны. Таким образом, производительность приложения увеличивается за счет сокращения расходов оперативной памяти и процессорного времени на работу GC.
На сегодня все!
Я понимаю, что информации много и осознать ее с первого раза не получится. Но есть и плюс: необходимости помнить все подробности реализации различных GC нет необходимости:)

Если что-то непонятно или не получается – welcome в комменты к посту или в лс:)
Канал: https://t.me/+relA0-qlUYAxZjI6
Мой тг: https://t.me/ironicMotherfucker
Дорогу осилит идущий!