Базовые алгоритмы
Mark-sweep: пометить достижимые, освободить остальное — просто, но оставляет фрагментацию. Mark-compact: после пометки уплотнить живые объекты к началу области — убирает фрагментацию ценой перемещения. Copying: копировать живых в другое пространство — быстро и без фрагментации, но требует резервного места (как в young).
Реальные сборщики комбинируют их: например, copying в молодом поколении и mark-compact в старом.

Stop-the-world и safepoints
Часть работы GC требует остановки приложения (stop-the-world): чтобы безопасно двигать объекты и менять ссылки, потоки должны замереть в safepoint — заранее определённой точке, где состояние стека согласовано.
Длительность STW-пауз — главный враг латентности. Эволюция сборщиков — это во многом борьба за то, чтобы делать больше работы конкурентно с приложением и сокращать STW.
Конкурентная маркировка
Современные сборщики маркируют живые объекты параллельно с работающим приложением. Проблема: приложение меняет ссылки во время маркировки. Её решают барьерами записи и алгоритмами вроде SATB (snapshot-at-the-beginning) или incremental update, гарантирующими, что ни один живой объект не будет ошибочно собран (tri-color invariant).
Цена конкурентности — небольшой постоянный оверхед на барьерах и возможность «плавающего мусора» (floating garbage), который соберётся в следующий цикл.
Сборщики JVM
Serial — однопоточный, для маленьких куч/контейнеров. Parallel — многопоточный, максимизирует throughput (полезную работу), но с заметными паузами; именно он был дефолтом до JDK 9. G1 (дефолт с JDK 9) делит кучу на регионы, собирает в первую очередь самые «мусорные» (garbage-first) и нацелен на предсказуемые паузы по цели -XX:MaxGCPauseMillis.
ZGC и Shenandoah — конкурентные сборщики со сверхкороткими паузами (суб-миллисекунда), почти не зависящими от размера кучи, за счёт перемещения объектов конкурентно с приложением (colored pointers / load barriers). Устаревший CMS удалён — его не предлагают.
# Пропускная способность важнее пауз (батчи, offline-обработка)
java -XX:+UseParallelGC -Xms4g -Xmx4g -jar app.jar
# Дефолт: сбалансированно, с целью по длительности паузы
java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms4g -Xmx4g -jar app.jar
# Жёсткий SLA по задержке на большой куче
java -XX:+UseZGC -Xms16g -Xmx16g -jar app.jar
# Какой сборщик выбран на самом деле:
java -XX:+PrintFlagsFinal -version | grep -E 'UseG1GC|UseZGC|UseParallelGC'
Какой выбрать
Выбор — это компромисс throughput против latency. Батч/аналитика, где важна общая пропускная способность и паузы терпимы — Parallel. Сервисы с требованиями к задержкам — G1 по умолчанию, а при жёстких SLA на хвостовые задержки и больших кучах — ZGC.
Решение подтверждают измерением (GC-логи, метрики пауз), а не выбирают «на глаз».
Как отвечать на собеседовании
Назовите три базовых алгоритма и их компромиссы (фрагментация/перемещение/резерв), объясните STW через safepoints и зачем конкурентная маркировка с барьерами, и сравните сборщики: Parallel (throughput) → G1 (баланс, дефолт, регионы) → ZGC (суб-мс паузы на больших кучах). Упомяните, что CMS устарел и удалён.