Как работают типы ссылок в Java (strong, weak, soft, phantom)?
Что проверяют
Понимаете ли вы, как сборщик мусора решает судьбу объекта, и умеете ли применить это на Android: удержание Activity, кэши, поиск утечек. Следом почти всегда спрашивают, почему SoftReference плохо подходит для кэша картинок и чем его заменить.
Как отвечать
Начните с достижимости: сборщик смотрит, каким самым сильным путём объект доступен из корней, и от этого зависит, когда его можно убрать.
- strong: обычная ссылка
val user = User(). Пока есть хоть одна цепочка сильных ссылок от корня, объект живёт; - soft (
SoftReference): очищается при нехватке памяти, гарантированно доOutOfMemoryError; - weak (
WeakReference): очищается при первой же сборке, в которой объект доступен только через слабые ссылки; - phantom (
PhantomReference):get()всегда возвращаетnull. Работает только вместе сReferenceQueueи нужна, чтобы узнать, что объект уже недостижим, и освободить внешний ресурс.
На Android самое частое применение слабой ссылки: колбэк или Handler, который может пережить экран, держит Activity через WeakReference и не мешает её собрать после поворота. На слабых ссылках с очередью построен LeakCanary: он оборачивает уничтоженную Activity в слабую ссылку и, если после сборки она не очищена, считает её утечкой и снимает дамп кучи.
Про кэш скажите прямо: документация Android не рекомендует SoftReference для кэширования. У рантайма нет информации, какие объекты ценнее, и чистит он их неудачно. Для битмапов и данных берут LruCache с ограничением по размеру, а загрузчики изображений держат свой кэш.
Где спотыкаются
Путают слабую и мягкую ссылку и говорят, что WeakReference живёт «пока хватает памяти». Это поведение мягкой, слабая исчезает на ближайшей сборке.
Вызывают ref.get() дважды подряд: первый раз для проверки на null, второй для работы. Между вызовами объект может исчезнуть, поэтому результат сохраняют в локальную переменную и проверяют уже её.
Лечат утечку слабой ссылкой вместо правильного жизненного цикла. Надёжнее отписываться в onDestroy или onStop, использовать viewLifecycleOwner и корутины в lifecycleScope, которые отменяются сами.