🧠 Розділ 3 · Питання #10

Що таке Old Generation (Tenured)

Для очищення старшого покоління застосовуються два класичні підходи:


🟢 Junior Level

Old Generation (Старше покоління, Tenured) — це область купи (Java Heap), призначена для зберігання довгоживучих об’єктів, які успішно пережили кілька циклів збирань сміття в Young Generation або були розміщені тут безпосередньо через свій великий розмір.


Основні характеристики Old Generation:

  • Розмір: Займає переважну частину купи (за замовчуванням при -XX:NewRatio=2 на Old Gen припадає $2/3$ всієї пам’яті Heap).
  • Патерн об’єктів: Об’єкти тут стабільні й помирають вкрай повільно (синглтони Spring, пули з’єднань HikariCP, довгоживучі кеші, сесії користувачів).
  • Вартість збирання: Збирання сміття в Old Generation вимагає значно більше часу та ресурсів CPU, ніж у Young Generation, оскільки сканується величезний масив даних.

Як об’єкти потрапляють в Old Generation:

  1. Природне дорослішання (Tenuring): Об’єкт пережив у Young Gen задану кількість збирань Minor GC (за замовчуванням до 15 циклів).
  2. Переповнення Survivor (Survivor Overflow): Обсяг вижилих після Minor GC об’єктів перевищив місткість регіону Survivor, і надлишки були виштовхнуті в Old Gen.
  3. Великі об’єкти (Humongous Objects): Масиви, розмір яких перевищує половину регіону в G1 GC, алокуються в Old Gen безпосередньо, оминаючи Eden.

🟡 Middle Level

Алгоритми очищення: Mark-Sweep vs Mark-Compact

Для очищення старшого покоління застосовуються два класичні підходи:

Алгоритм Опис роботи Плюси Мінуси
Mark-Sweep 1. Розмітка живих об’єктів.
2. Очищення мертвих, додавання їхніх координат до списків вільних блоків (Free Lists).
Висока швидкість, можливість фонового виконання Фрагментація пам’яті: купа перетворюється на «швейцарський сир» із безліччю розрізнених дірок.
Mark-Compact 1. Розмітка живих об’єктів.
2. Зсув усіх вижилих об’єктів на початок пам’яті єдиним блоком (ущільнення).
Нульова фрагментація: відновлює швидкий bump-the-pointer Довгі паузи Stop-The-World (потрібно кілька проходів для оновлення всіх посилань).

Аварія продуктивності: Promotion Failure

Promotion Failure (Збій просування) — критична ситуація під час виконання Minor GC:

  1. Збирач сміття намагається перемістити вижилий об’єкт із Survivor в Old Generation.
  2. В Old Generation немає неперервного шматка пам’яті потрібного розміру (через фрагментацію або вичерпання обсягу).
  3. Наслідок: JVM екстрено зупиняє всі потоки застосунку та запускає аварійний Full GC (Mark-Compact по всій купі), заморожуючи сервіс на секунди або десятки секунд.

Очищення Old Gen у G1: Механіка Mixed GC та параметр IHOP

На відміну від старих збирачів, G1 не намагається очистити весь Old Gen за один раз у межах однієї гігантської паузи:

Купа G1:
[Young] [Young] [Old-1] [Old-2] [Old-3] [Free] ...
                  │
                  ▼
Вибираються лише регіони Old з максимальним % сміття ("Garbage-First")
  1. IHOP (Initiating Heap Occupancy Percent):
    • Поріг заповнення купи, за якого запускається фонова фаза розмітки Old Gen (за замовчуванням 45%).
    • У сучасному JDK за замовчуванням увімкнено адаптивний розрахунок -XX:+G1UseAdaptiveIHOP: JVM сама вимірює швидкість заповнення Old Gen та час розмітки, динамічно зсуваючи поріг старту, щоб збирач встиг розмітити пам’ять до її вичерпання.
  2. Mixed GC (Змішане збирання):
    • G1 збирає всі регіони Young Gen плюс кілька найбільш «брудних» регіонів Old Gen (де сміття найбільше).
    • Очищення Old Gen відбувається інкрементно порціями за кілька циклів (-XX:G1MixedGCCountTarget=8), вкладаючись у цільове вікно паузи (-XX:MaxGCPauseMillis=200).

🔴 Senior Level

Плаваюче сміття (Floating Garbage)

Під час конкурентної розмітки (Concurrent Marking у G1, ZGC, Shenandoah) застосунок продовжує виконуватися паралельно зі збирачем сміття:

  • Якщо під час розмітки потік застосунку обнуляє посилання myObj = null;, об’єкт myObj уже був позначений збирачем як «живий» на момент знімка SATB (Snapshot-At-The-Beginning).
  • Цей об’єкт фактично став мертвим, але збирач не видалить його в поточному циклі.
  • Таке сміття називається Floating Garbage (Плаваюче сміття). Воно неминуче займає пам’ять до наступного циклу GC. Під час розрахунку сайзингу купи завжди закладають 20–30% резерву під плаваюче сміття.

Конкурентне ущільнення в ZGC та Shenandoah

У традиційних збирачах переміщення довгоживучих об’єктів в Old Gen вимагало повної зупинки застосунку (STW), оскільки переміщення об’єкта змінює його фізичну адресу в пам’яті.

У сучасних low-latency збирачах ущільнення Old Gen переведено в конкурентний режим:

  • ZGC: Використовує Colored Pointers (біти посилання вказують статус об’єкта) та Load Barriers на читання. Якщо потік читає об’єкт з Old Gen, який перебуває в процесі переміщення, Load Barrier сам перенаправляє виклик на нову адресу й оновлює покажчик на льоту.
  • Shenandoah: Використовує покажчики Брукса (Brooks Pointers) або LRB (Load-Reference Barriers) для прозорої переадресації потоків до нової копії об’єкта в процесі евакуації.

Моніторинг та діагностика в логах

Ознаки проблем зі старшим поколінням у логах -Xlog:gc*:

[GC(45) Pause Young (Normal) (G1 Evacuation Pause) 1800M->1800M(2048M) ... (to-space exhausted)]
[GC(46) Pause Full (System.gc()) 1800M->400M(2048M) 2150.320ms]
  • to-space exhausted — сигнал про нестачу місця для просування об’єктів в Old Gen.
  • Pause Full тривалістю 2150 мс — катастрофічний Stop-The-World, що означає, що G1 не впорався інкрементно і звалився в глобальний Full GC.

🎯 Шпаргалка для інтерв’ю

30-секундна відповідь (Elevator Pitch)

«Old Generation (Tenured) — це область купи, де розміщуються довгоживучі об’єкти (що пережили MaxTenuringThreshold у Young Gen) та великі масиви (Humongous у G1). Збирання тут дорожче, ніж у Young Gen. У класичних збирачах воно виконується через Mark-Sweep-Compact, а в G1 — інкрементно через фази Concurrent Marking та Mixed GC, що стартують за порогом IHOP (за замовчуванням 45%). Головні виробничі ризики Old Gen — Promotion Failure (коли об’єкти з Young не можуть поміститися в Old через фрагментацію, викликаючи Full GC) та накопичення Floating Garbage (плаваючого сміття) під час паралельної розмітки».


3–4 каверзних запитання з відповідями

  1. Що таке Promotion Failure і чому це катастрофа для затримок (Latency) сервісу?
    • Відповідь: Promotion Failure виникає під час Minor GC, коли вижилі об’єкти з Eden/Survivor не можуть бути скопійовані в Old Generation через відсутність неперервного вільного блоку пам’яті (спричиненого сильною фрагментацією або переповненням Old Gen). У цей момент JVM не може продовжити роботу й ініціює аварійний однопотоковий або паралельний Full GC зі Stop-The-World паузою на всю купу, що призводить до завмирання сервісу на секунди.
  2. У чому різниця між Major GC, Mixed GC та Full GC?
    • Відповідь:
      • Major GC: Очищення суворо старшого покоління (Old Generation).
      • Mixed GC (у збирачі G1): Очищає цілком усе Young Generation плюс кілька вибраних найбільш забруднених регіонів Old Generation.
      • Full GC: Глобальне збирання всієї віртуальної машини цілком: очищається Young Gen, Old Gen та вивантажуються невикористовувані класи з Metaspace. Супроводжується найважчою паузою STW.
  3. Що таке параметр IHOP у G1 GC і як працює Adaptive IHOP?
    • Відповідь: IHOP (Initiating Heap Occupancy Percent) — це поріг заповнення купи (за замовчуванням 45%), при перевищенні якого G1 ініціює фоновий цикл конкурентної розмітки Old Generation. Починаючи з Java 9, увімкнено адаптивний режим: JVM постійно вимірює швидкість алокації (Allocation Rate) та фактичну тривалість циклу розмітки, динамічно підлаштовуючи поріг так, щоб розмітка завершувалася рівно перед тим, як пам’ять Old Gen вичерпається.
  4. Що таке «Плаваюче сміття» (Floating Garbage)?
    • Відповідь: Це об’єкти, які стали недосяжними (втратили посилання) вже після того, як збирач сміття зробив знімок графа об’єктів на початку фази конкурентної розмітки (SATB). Збирач вважає їх живими до кінця поточного циклу, тому вони не можуть бути зібрані просто зараз і продовжують тимчасово займати пам’ять до наступного циклу GC.

Типові помилки та Red Flags

  • 🚩 Твердження: «Full GC у production — це нормальне явище» (у сучасних системах із налаштованим G1 або ZGC Full GC свідчить про помилку сайзингу, витік пам’яті або нестачу ресурсів).
  • 🚩 Спроба лікувати Promotion Failure простим роздуванням Heap без усунення генерації сміття чи налаштування регіонів.
  • 🚩 Ігнорування плаваючого сміття (Floating Garbage) під час розрахунку запасу пам’яті під highload-навантаження.
  • 🚩 Плутанина між термінами Minor GC, Mixed GC та Full GC.

Пов’язані теми