Что такое циклические ссылки
Что проверяют
Знаете ли вы, как на самом деле работает сборщик мусора в ART, и не путаете ли его с подсчётом ссылок. Вопрос почти всегда ведёт к утечкам памяти, поэтому смотрят, назовёте ли вы настоящую причину утечки или повторите расхожий миф.
Как отвечать
Начните с главного: два объекта, ссылающиеся друг на друга, сами по себе утечкой не являются. Сборщик в ART трассирующий, он обходит граф от корней и собирает всё, до чего не дошёл. Группа объектов, замкнутая сама на себя и недостижимая извне, собирается целиком. Подсчёт ссылок циклы действительно не осилил бы, но это про другие среды.
Утечка появляется, когда на короткоживущий объект держит ссылку долгоживущий. Классика в Android выглядит так: Activity или View остаётся достижимой из статического поля, синглтона, фонового потока, отложенного сообщения в очереди Handler, подписки или слушателя, зарегистрированного в системном сервисе. Экран пересоздали при повороте, а старый экземпляр вместе со всей иерархией вью остался жив.
Отдельно вспомните нестатические вложенные и анонимные классы: они держат неявную ссылку на внешний экземпляр, поэтому долгоживущий колбэк утаскивает за собой весь экран.
Лечение называйте по шагам: отписываться в парном методе жизненного цикла, во фрагменте обнулять биндинг в onDestroyView и подписываться на viewLifecycleOwner, состояние держать во ViewModel, в синглтон передавать только applicationContext, а догадки проверять профайлером памяти или детектором утечек.
Где спотыкаются
Уверенно заявляют, что цикл ссылок не даёт объектам собраться. Это верно для подсчёта ссылок и неверно для JVM и ART.
Предлагают WeakReference как универсальное лекарство и ставят его везде. Слабая ссылка прячет симптом и добавляет неопределённость в момент очистки, а причина, живая подписка, остаётся на месте.
Считают, что обнуления полей в onDestroy достаточно. Если ссылку держит чужой объект, ваше поле ни при чём, да и сам метод может не вызваться.