Сталкивались ли с утечками памяти?
Что проверяют
Был ли у вас реальный опыт поиска утечек в Android-приложении или вы знаете только определение. Интервьюер хочет услышать, как вы нашли проблему, чем подтвердили и как исправили, а следом обычно спрашивает, почему фрагменты протекают чаще активностей.
Как отвечать
Утечка в Android почти всегда устроена одинаково: объект с коротким жизненным циклом (Activity, Fragment, View) держит кто-то долгоживущий, и сборщик мусора не может его убрать. Назовите типичные источники:
- статическое поле или синглтон с
Context: держит всю Activity с иерархией View. Долгоживущим объектам отдаютapplicationContext; - неотписанные слушатели:
BroadcastReceiver, колбэки сенсоров, листенеры сторонних SDK. Отписка должна стоять в парном колбэке жизненного цикла; - анонимный
HandlerилиRunnableс отложенным сообщением: неявно ссылается на внешний класс; - View во фрагменте: фрагмент живёт дольше своей View, поэтому binding обнуляют в
onDestroyView, а LiveData и Flow подписывают наviewLifecycleOwner; - корутины в
GlobalScopeи неотменённые подписки RxJava.
Инструменты: LeakCanary подключается одной строкой как debugImplementation, сам ловит удержанные Activity, Fragment и ViewModel и показывает цепочку ссылок до корня. Для разбора глубже есть Memory Profiler в Android Studio и heap dump.
Рассказ стройте на своём случае: симптом (рост памяти при поворотах экрана, OutOfMemoryError в Crashlytics), чем нашли, какая была цепочка, что поменяли и как проверили. Если в продакшене с утечками не сталкивались, скажите прямо и опишите, как искали бы: включить LeakCanary, прогнать сценарий с поворотами и возвратом назад, прочитать трассу.
Где спотыкаются
Лечат всё через WeakReference. Это маскирует проблему владения: правильнее разорвать ссылку в нужный момент жизненного цикла или вообще не отдавать Activity долгоживущему объекту.
Путают утечку с высоким потреблением памяти. Большие битмапы без даунсэмплинга дают OOM, но это не утечка, и лечится оно по-другому.
Обнуляют binding в onDestroy фрагмента вместо onDestroyView. Фрагмент в back stack живёт дальше, и его старая View висит в памяти до возврата на экран.