Що таке ZGC?
4. Моніторте Allocation Stall у логах 5. CPU overhead 5-15% — закладайте при плануванні
🟢 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
- Java 21+ — використовуйте Generational ZGC
- ConcGCThreads = кількість ядер / 4
- SoftMaxHeapSize для контейнерів
- Моніторте Allocation Stall у логах
- 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 паузи]]