🧠 Раздел 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.

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