Garbage collector

Сборщик мусора автоматически переиспользует память объектов, которые больше недостижимы из работающей программы. Недостижимость важнее количества ссылок: цикл из двух объектов также собирается, если до него нельзя дойти от GC roots.

GC roots

К типичным корням относятся:

  • локальные ссылки активных стековых frames;
  • статические поля загруженных классов;
  • активные Java-потоки;
  • ссылки из JNI и внутренних структур JVM.

Трассировка начинает с корней и помечает достижимый граф. Всё, что не помечено, считается кандидатом на освобождение.

Основные операции

ОперацияНазначение
Markнайти достижимые объекты
Sweepосвободить непомеченные области
Compactсдвинуть живые объекты и убрать фрагментацию
Copyперенести живые объекты в другую область

Разные collectors сочетают эти операции и по-разному распределяют работу между потоками приложения и GC. Stop-the-world означает, что Java-потоки приостановлены на определённой фазе; это не означает, что весь цикл сборки обязательно выполняется в одной длинной паузе.

Поколения

Большинство объектов живёт недолго. Generational collectors используют это: новые объекты сначала попадают в молодое поколение, пережившие сборки могут перемещаться или учитываться как старые. Конкретные названия областей и алгоритмы зависят от collector; не у каждого GC схема одинакова.

В Java 17 доступны, среди прочих, Serial, Parallel, G1, ZGC и Shenandoah (наличие может зависеть от сборки JDK). G1 является обычным выбором по умолчанию в HotSpot Java 17, но collector выбирают по требованиям к throughput, паузам и памяти, а не по одному названию.

Ссылочные типы

SoftReference, WeakReference и PhantomReference дают разные гарантии достижимости. Они не заменяют явную политику кэша. finalize устарел и ненадёжен; для ресурсов применяют try-with-resources и при необходимости Cleaner.

try (var stream = Files.lines(path)) {
    return stream.count();
} // файл закрывается детерминированно, независимо от GC

Диагностика

Высокая частота сборок — симптом, а не диагноз. Проверяют allocation rate, размер живого набора, паузы, heap dump и удерживающие ссылки. Вызов System.gc() — только просьба JVM и не гарантирует немедленную сборку.

Области памяти: Стек и куча в Java.

Вопросы для собеседования

  1. Соберёт ли GC циклические ссылки? Да, если цикл недостижим от roots.
  2. Что такое stop-the-world? Период остановки потоков приложения для фазы GC.
  3. Почему GC не закрывает файл вовремя? Достижимость и внешние ресурсы — разные задачи; нужен close.
  4. Что такое memory leak в Java? Ненужные объекты остаются достижимыми.
  5. Гарантирует ли System.gc() сборку? Нет.
  6. Зачем compaction? Уменьшить фрагментацию и получить непрерывное свободное место.