Вопрос 14 · Раздел 3

Что такое ZGC?

4. Мониторьте Allocation Stall в логах 5. CPU overhead 5-15% — закладывайте при планировании

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

ZGC (Z Garbage Collector) — сборщик с минимальными паузами (< 1 мс), не зависящими от размера Heap. Throughput на 5-15% ниже, чем у G1.

Главное преимущество: Паузы практически не зависят от размера Heap и обычно < 1 мс. При экстремальной нагрузке возможны отклонения.

Когда использовать:

  • Real-time системы
  • High-frequency trading
  • Когда SLA < 10 мс

🟡 Middle Level

Ключевые технологии

1. Colored Pointers:

64-битный указатель:
  Биты 0-41: адрес объекта
  Биты 42-45: метаданные (состояние GC)

→ GC понимает статус объекта, просто прочитав указатель
→ Без обращения к памяти объекта!

Colored Pointers — в 64-битных указателях ZGC использует верхние
биты для хранения метаданных (поколение, finalizable, weak и т.д.).
JVM «раскрашивает» указатели, экономя отдельную структуру данных.

2. Load Barriers:

При каждом чтении ссылки:
  → Проверить цвет указателя
  → Если объект переехал → исправить адрес (Self-healing)
  → overhead ~5-15% CPU

Load Barriers — проверки при КАЖДОМ чтении ссылки. Если указатель
«неправильный» (объект перемещён), Load Barrier автоматически
исправляет указатель (Self-Healing).

3. Concurrent Evacuation:

Объекты перемещаются БЕЗ остановки приложения!
  → В отличие от G1 (Evacuation = STW)
  → Пауза только для сканирования GC Roots

ZGC vs G1

Критерий G1 ZGC
Пауза 20-200 мс < 1 мс
Throughput Высокий -5-10%
Max Heap 64 ГБ 16 ТБ
Barriers Write Load

Generational ZGC (Java 21+)

До Java 21: одно поколение
  → Сканировал весь Heap при каждой сборке

До Java 21 ZGC был без поколений — это вызывало Allocation Stalls
при высоких allocation rate. Generational ZGC (Java 21+) решает
эту проблему.

Java 21+: Young и Old поколения
  → -50% CPU overhead
  → Allocation Stalls почти исчезли

Когда НЕ использовать

❌ CPU ограничен (90%+) → Load Barriers добьют
❌ Heap < 4 ГБ → G1 эффективнее
❌ 32-битные системы → ZGC требует 64-бит

Тюнинг

-XX:+UseZGC -XX:+ZGenerational  # Java 21+
-XX:SoftMaxHeapSize=24g          # Агрессивный возврат памяти
-XX:ConcGCThreads=4              # Потоки GC

🔴 Senior Level

Multi-mapping

Один физический участок памяти маппится на 3 виртуальных адреса
  → Разные комбинации бит метаданных
  → ОС не видит конфликта указателей
  → Позволяет работать с "цветными" указателями

Multi-mapping — техника отображения одной области Heap на несколько
виртуальных адресов, чтобы кодировать метаданные в адресах указателей.

Self-Healing Pointers

Load Barrier при чтении:
  1. Прочитать указатель
  2. Проверить бит Remapped
  3. Если не Remapped → объект переехал
  4. Вычислить новый адрес
  5. Обновить указатель в Heap (CAS)
  6. Вернуть новый адрес
  
→ Указатель "лечится" при каждом чтении

Allocation Stall Deep Dive

Причина: GC не успевает размечать мусор
  → Потоки приложения ждут освобождения памяти
  
Решения:
  1. Увеличить Heap
  2. Увеличить ConcGCThreads
     `ConcGCThreads` по умолчанию = количество ядер / 4.
     Увеличьте, если видите Allocation Stall; уменьшите, если GC забирает слишком много CPU.
  3. Generational ZGC (Java 21+)
  4. Уменьшить Allocation Rate

Best Practices

  1. Java 21+ — используйте Generational ZGC
  2. ConcGCThreads = количество ядер / 4
  3. SoftMaxHeapSize для контейнеров
  4. Мониторьте Allocation Stall в логах
  5. CPU overhead 5-15% — закладывайте при планировании

Резюме для Senior

  • ZGC = Colored Pointers + Load Barriers + Conc Evacuation
  • Паузы < 1 мс константны, не зависят от Heap
  • Generational (Java 21+) = -50% overhead
  • Self-healing указатели через Load Barriers
  • Allocation Stall = главный риск
  • CPU overhead 5-15% — цена за low latency

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

Обязательно знать:

  • ZGC: паузы < 1 мс, не зависят от размера Heap (до 16 ТБ); throughput на 5-15% ниже G1
  • Colored Pointers: верхние биты 64-битного указателя хранят метаданные (поколение, состояние GC)
  • Load Barriers: при каждом чтении ссылки проверка цвета указателя + Self-healing (если объект переехал)
  • Concurrent Evacuation: объекты перемещаются БЕЗ STW (в отличие от G1, где Evacuation = STW)
  • Multi-mapping: один физический участок Heap маппится на 3 виртуальных адреса для кодирования метаданных
  • Generational ZGC (Java 21+): разделение на Young/Old → -50% CPU overhead, Allocation Stalls почти исчезли
  • Когда НЕ использовать: CPU bound (90%+), Heap < 4 ГБ, 32-битные системы

Частые уточняющие вопросы:

  • Почему ZGC медленнее G1 по throughput? — Load Barriers при каждом чтении ссылки добавляют 5-15% CPU overhead
  • Что такое Allocation Stall в ZGC? — GC не успевает размечать мусор → потоки приложения ждут; решения: увеличить Heap, ConcGCThreads, или Generational ZGC
  • Что такое Self-Healing указатели? — Load Barrier читает указатель → проверяет бит Remapped → если объект переехал, вычисляет новый адрес и обновляет указатель (CAS)
  • Зачем Multi-mapping? — Одна область Heap на 3 виртуальных адреса → кодирование метаданных в адресах без конфликта указателей

Красные флаги (НЕ говорить):

  • «ZGC использует Write Barriers» — ZGC использует Load Barriers (при чтении); G1 использует Write Barriers (при записи)
  • «ZGC работает на 32-битных системах» — ZGC требует 64-битную систему (Colored Pointers используют верхние биты)
  • «ZGC всегда лучше G1» — для CPU-bound или batch processing G1 даёт лучший throughput

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

  • [[12. Какие алгоритмы GC существуют]]
  • [[13. Что такое G1 GC]]
  • [[15. Что такое Shenandoah GC]]
  • [[16. Что такое stop-the-world]]
  • [[17. Какие GC минимизируют stop-the-world паузы]]