Когда объекты собираются сборщиком мусора
Что проверяют
Понимаете ли вы, что в ART объект собирается не «когда на него нет ссылок», а когда он недостижим от корней, и что момент сборки выбирает рантайм. Следом почти всегда спрашивают про утечки памяти в Android и как их искать.
Как отвечать
Сборщик в ART трассирующий, он обходит граф объектов от GC roots:
- корни: локальные переменные и параметры в стеках живых потоков, статические поля загруженных классов, JNI-ссылки, захваченные мониторы;
- достижимость: всё, до чего можно дойти от корней по сильным ссылкам, живёт. Остальное мусор, включая циклы: два объекта, ссылающиеся друг на друга, собираются, если к ним не ведёт путь от корня;
- слабые ссылки:
WeakReferenceне удерживает объект и очищается при сборке, если сильных ссылок не осталось. Для кэшей на Android вместоSoftReferenceсоветуютLruCacheс явным лимитом.
Когда именно запустится сборка, решает рантайм: по давлению аллокаций, по росту кучи до порога, в фоне. obj = null делает объект кандидатом, но память не освобождает, а System.gc() лишь подсказка. С Android 8.0 основной сборщик ART это Concurrent Copying, который работает в основном параллельно с приложением, а в более новых версиях у него есть поколенческий режим: молодые объекты собираются часто и дёшево.
Для Android важнее всего утечки, то есть объекты, которые остаются достижимыми дольше, чем нужны. Типичные: Activity или View в статическом поле или синглтоне, анонимный Runnable в Handler с отложенным сообщением, корутина в GlobalScope, захватившая экран, ViewModel со ссылкой на Context активити. Искать их помогают LeakCanary и Memory Profiler в Android Studio с дампом кучи.
Где спотыкаются
Описывают подсчёт ссылок. В Java и Kotlin его нет, поэтому циклические ссылки сами по себе утечкой не являются.
Уверены, что null или System.gc() сразу освобождают память. Это лишь меняет достижимость, а сборка произойдёт, когда решит ART.
Рассказывают про HotSpot, G1 и ZGC. На Android другой рантайм, и ответ про JVM-флаги показывает, что вы не различаете платформы.