🧠 Раздел 3 · Вопрос #12

Какие алгоритмы GC существуют

В виртуальной машине Java необходимо четко различать теоретические базовые алгоритмы очистки памяти (фундаментальные подходы) и конкретные реализации сборщиков мусора (Garbage C...


🟢 Junior Level

В виртуальной машине Java необходимо четко различать теоретические базовые алгоритмы очистки памяти (фундаментальные подходы) и конкретные реализации сборщиков мусора (Garbage Collectors) в JVM HotSpot, которые комбинируют эти алгоритмы для разных поколений кучи.


3 базовых алгоритма очистки памяти

Алгоритм Принцип работы Плюсы Минусы Где применяется
Copying (Копирование) Память делится на две зоны. Живые объекты копируются из активной зоны в свободную, а старая зона очищается целиком. Исключительная скорость, нулевая фрагментация Требует двойного запаса памяти Young Generation (Eden $\rightarrow$ Survivor)
Mark-Sweep (Разметка и удаление) 1. Разметка: обход графа от GC Roots и маркировка живых объектов.
2. Очистка: удаление неразмеченных объектов.
Не требует перемещения объектов, экономен по памяти Высокая фрагментация: память покрывается изолированными «дырами» Старые сборщики Old Gen (CMS)
Mark-Compact (Разметка и уплотнение) 1. Разметка живых объектов.
2. Сдвиг всех выживших объектов в начало региона единым плотным монолитом.
Нулевая фрагментация: восстанавливает быстрый bump-the-pointer Долгие паузы Stop-The-World (многопроходный пересчет ссылок) Full GC в G1 и Parallel Old

Бытовая аналогия

  • Copying: Переезд в новую отремонтированную квартиру: вы забираете с собой только нужные вещи, а старую квартиру с мусором отдаете под снос.
  • Mark-Sweep: Пройти по комнате с мусорным пакетом и выкинуть фантики из углов. Вещи остались на местах, но на полу образовались хаотичные пустоты.
  • Mark-Compact: Выбросить фантики, а всю оставшуюся мебель плотно сдвинуть к одной стене, освободив сплошное свободное пространство.

🟡 Middle Level

Палитра сборщиков мусора в HotSpot JVM

Виртуальная машина предоставляет набор готовых сборщиков под разные типы нагрузок:

┌────────────────────────────────────────────────────────────────────────┐
│                     СБОРЩИКИ МУСОРА В HOTSPOT                          │
├─────────────────┬──────────────┬──────────────┬────────────────────────┤
│ Сборщик мусора  │ Паузы (STW)  │ Throughput   │ Рекомендуемое применение│
├─────────────────┼──────────────┼──────────────┼────────────────────────┤
│ Serial GC       │ 100–500 мс   │ Низкий (1 CPU)│ Микроконтейнеры, CLI  │
│ Parallel GC     │ 100–1000 мс  │ Максимум     │ Batch, Big Data, Hadoop│
│ G1 GC (Def)     │ 20–200 мс    │ Высокий      │ Web-сервисы, Spring Boot│
│ ZGC             │ < 1 мс       │ Средний (-10%)│ HFT, FinTech, Real-time│
│ Shenandoah      │ < 10 мс      │ Средний (-10%)│ Альтернативный low-lat │
│ Epsilon GC      │ 0 мс (нет GC)│ Абсолютный   │ Бенчмарки, Lambdas    │
└─────────────────┴──────────────┴──────────────┴────────────────────────┘

Треугольник компромиссов GC (Trade-off Triangle)

Невозможно создать сборщик мусора, идеальный по всем параметрам. Архитектурный выбор всегда диктуется компромиссом между тремя факторами:

                  Latency (Минимальные паузы)
                         ▲
                        / \
                       /   \
  Throughput ◄───────┴───────► Footprint
(Пропускная способность)     (Оверхед по памяти/CPU)
  • Parallel GC жертвует Latency (длинные паузы) ради максимального Throughput (все 100% CPU отдаются расчету данных).
  • ZGC жертвует Throughput (тратит 5–15% CPU на барьеры чтения) ради ультранизкого Latency (паузы $< 1$ мс).
  • G1 GC — золотая середина: адаптивно балансирует паузы и пропускную способность для большинства enterprise-задач.

🔴 Senior Level

Трехцветная разметка (Tri-Color Marking)

В основе всех современных параллельных и конкурентных сборщиков (G1, ZGC, Shenandoah) лежит абстракция трех цветов:

┌────────────────────────────────────────────────────────┐
│ ⚪ БЕЛЫЙ: Непосещенный объект. В конце фазы разметки   │
│           все оставшиеся белые объекты — это МУСОР.    │
├────────────────────────────────────────────────────────┤
│ 🔘 СЕРЫЙ: Объект посещен, но его дочерние поля-ссылки  │
│           еще не просканированы сборщиком.             │
├────────────────────────────────────────────────────────┤
│ ⚫ ЧЕРНЫЙ: Объект гарантированно жив, и ВСЕ его поля   │
│           успешно просканированы.                      │
└────────────────────────────────────────────────────────┘

Проблема конкурентной разметки (Lost Object Problem):

Во время параллельной работы приложения поток выполнения (Mutator) может перепривязать ссылку:

  1. Поток забирает ссылку на Белый объект $C$ у Серого объекта $B$.
  2. Поток записывает ссылку на этот Белый объект $C$ в поле Черного объекта $A$.
  3. Поскольку объект $A$ уже черный, сборщик к нему не вернется. Серый объект $B$ ссылку потерял.
  4. Катастрофа: Живой объект $C$ остался белым и будет ошибочно уничтожен сборщиком мусора!

Решение проблемы: SATB vs Incremental Update vs Load Barriers

Разные GC решают проблему потери объектов принципиально разными механизмами:

1. SATB (Snapshot-At-The-Beginning) — используется в G1:

  • Применяет Write Barrier (Барьер записи): перед тем как затереть старую ссылку в B.field = null, JVM перехватывает старое значение ссылки на объект $C$ и принудительно помещает его в локальный буфер разметки.
  • Объект $C$ помечается серым и гарантированно выживает.
  • Trade-off: Возникает Floating Garbage (Плавающий мусор): если объект $C$ действительно больше никому не нужен, он все равно переживет текущий цикл сборки.

2. Incremental Update — использовался в CMS:

  • При добавлении ссылки на белый объект в черный A.field = C, Write Barrier перекрашивает черный объект $A$ обратно в Серый, заставляя GC повторно просканировать его поля перед финишем.

3. Load Barriers (Барьеры чтения) — используется в ZGC:

  • Перехватывает не запись, а чтение ссылки Object o = a.field;.
  • Проверяет метаданные цвета указателя (Colored Pointers). Если объект перемещается, Load Barrier на лету обновляет указатель прямо в потоке (Self-Healing), делая процесс эвакуации полностью прозрачным для приложения.

Epsilon GC: No-Op сборщик мусора

Включенный флагом -XX:+UseEpsilonGC, этот сборщик реализует только интерфейс аллокатора памяти.

  • При вызове new он инкрементирует указатель кучи (Bump-the-pointer).
  • Он никогда не собирает мусор. Когда куча заполняется, процесс немедленно падает с java.lang.OutOfMemoryError: Java heap space.
  • Зачем нужен в реальном мире:
    1. Изолированные микро-бенчмарки (JMH): измерение чистой производительности кода без помех от фоновых потоков GC.
    2. Serverless-функции (AWS Lambda / Knative): микросервисы с временем жизни в несколько секунд, которым гарантированно хватает 256 МБ кучи на время выполнения одного запроса.

🎯 Шпаргалка для интервью

30-секундный ответ (Elevator Pitch)

«Базовые алгоритмы сборки мусора включают: Copying (быстрое копирование выживших без фрагментации, применяется в Young Gen), Mark-Sweep (разметка и удаление, вызывает фрагментацию памяти) и Mark-Compact (уплотнение со сдвигом объектов, устраняющее фрагментацию ценой пауз STW). В HotSpot эти алгоритмы объединены в готовые сборщики: Parallel GC (максимальный throughput для пакетных задач), G1 GC (сбалансированный стандарт Java 9+ с региональной структурой и адаптивными паузами), и сверхбыстрые ZGC / Shenandoah (паузы $< 1$ мс за счет конкурентного уплотнения с барьерами чтения). Для предотвращения потери живых объектов при конкурентной разметке G1 использует алгоритм SATB, а ZGC — Load Barriers».


3–4 каверзных вопроса с ответами

  1. В чем разница между базовыми алгоритмами GC и конкретными сборщиками в HotSpot?
    • Ответ: Базовые алгоритмы (Copying, Mark-Sweep, Mark-Compact) — это теоретические методы обхода и очистки памяти. Сборщики мусора в HotSpot (Parallel, G1, ZGC) — это сложные программные подсистемы, которые комбинируют эти алгоритмы для разных поколений памяти, управляя параллелизмом, планированием потоков, барьерами памяти и компромиссами между пропускной способностью (Throughput) и паузами (Latency).
  2. Что такое трехцветная разметка (Tri-Color Marking) и как G1 предотвращает ошибочное удаление объектов?
    • Ответ: Объекты делятся на Белые (непосещенные, кандидаты на мусор), Серые (посещенные, поля не проверены) и Черные (живые, все поля проверены). Если поток приложения стирает ссылку на белый объект у серого и передает ее черному, сборщик может не заметить живой белый объект и удалить его. G1 решает эту проблему через алгоритм SATB (Snapshot-At-The-Beginning): барьер записи перехватывает факт удаления ссылки и принудительно помечает старый белый объект как серый, сохраняя снимок графа на начало сборки.
  3. Чем принципиально отличаются барьеры записи (Write Barriers) в G1 от барьеров чтения (Load Barriers) в ZGC?
    • Ответ: Write Barrier в G1 срабатывает при модификации ссылок в объектах (obj.field = newRef), помечая грязные страницы в Card Table или буферизируя старые ссылки для SATB. Load Barrier в ZGC срабатывает при разыменовании ссылки (Ref r = obj.field). Если ссылка указывает на еще не перемещенный объект во время конкурентной эвакуации, Load Barrier на лету обновляет ссылку в памяти на новый адрес (self-healing), позволяя перемещать объекты без глобальных пауз Stop-The-World.
  4. В каких сценариях Parallel GC предпочтительнее современного ZGC или G1?
    • Ответ: В сценариях пакетной обработки данных (Batch Processing, Big Data ETL, научные вычисления, компиляция), где задержки ответа (Latency) не имеют значения, а критична только общая скорость завершения задачи. Parallel GC не тратит циклы CPU на фоновые барьеры чтения/записи и многопоточную синхронизацию разметки, обеспечивая максимально возможный Throughput на доступных ядрах процессора.

Типичные ошибки и Red Flags

  • 🚩 Утверждение: «ZGC всегда во всех сценариях лучше, чем Parallel GC или G1» (ZGC жертвует 5–15% Throughput и требует больше памяти на барьеры; для пакетных задач Parallel эффективнее).
  • 🚩 Смешивание понятий базового алгоритма (Mark-Sweep) и готового сборщика (G1 GC).
  • 🚩 Заблуждение: «Epsilon GC можно использовать в качестве обычного легковесного сборщика в проде» (Epsilon вообще не очищает память и гарантированно упадет с OOM при заполнении кучи).
  • 🚩 Непонимание механики барьеров памяти (Write Barriers vs Load Barriers).

Связанные темы