Что такое корни (roots) в garbage collection и как определяется достижимость объектов
Что проверяют
Понимаете ли вы, что сборщик мусора ищет не ненужные объекты, а недостижимые, и умеете ли назвать, откуда начинается обход. Отсюда растёт вся практика: почти каждая утечка в Java это объект, честно достижимый от корня и потому обязанный жить.
Как отвечать
Корни это ссылки, которые сборщик считает живыми по определению, не проверяя их достижимость. Назовите основные: локальные переменные и параметры в кадрах стека живых потоков, сами объекты потоков, статические поля загруженных классов, а значит и загрузчики классов, локальные и глобальные ссылки JNI, объекты с удерживаемым монитором, внутренние структуры виртуальной машины.
Достижимость определяется обходом от корней: помечаем всё, куда можно дойти по ссылкам, непомеченное считается мусором. Направление обхода именно такое, от корня к объектам, на которые он ссылается. Поэтому цикл из взаимных ссылок собирается без проблем, если снаружи на него никто не смотрит. В этом ключевое отличие трассировки от подсчёта ссылок.
Дальше уместны градации. Обычная ссылка удерживает всегда, мягкая переживает сборку, пока хватает памяти, слабая не удерживает вовсе, фантомная нужна для уборки ресурсов после освобождения. Финализация и очереди ссылок откладывают освобождение ещё на один цикл сборки.
Где спотыкаются
Говорят, что сборщик ищет объекты, которые ссылаются на корни. Обход идёт в обратную сторону, и путаница тут же вылезает в ответе про циклические ссылки.
Считают достижимость признаком нужности. Растущий кэш в статическом поле, неотписанный слушатель, поток с ссылкой на большой контекст: всё это достижимо и потому вечно, хотя не нужно никому.
Обещают, что обнуление переменной или вызов System.gc освободит память сразу. Первое работает, только если это была последняя ссылка, второе лишь просьба, а момент сборки решает сама виртуальная машина.