Зачем нужны soft references в Java
Что проверяют
Знаете ли вы четыре вида ссылок в Java и понимаете ли, чем soft отличается от weak для сборщика мусора. На Android следом почти всегда спрашивают, стоит ли строить на SoftReference кэш картинок. Правильный ответ: нет, и важно объяснить почему.
Как отвечать
Начните с пакета java.lang.ref:
- strong: обычная ссылка, пока она есть, объект не соберут;
SoftReference: объект собирается, когда рантайм решит, что памяти мало. Гарантия одна: все soft ссылки очищаются до того, как будет брошенOutOfMemoryError, а когда именно, решает конкретный GC;WeakReference: объект собирается на ближайшей сборке, если сильных ссылок нет. На этом построеныWeakHashMapи защита от утечек, когда колбэк держитActivity;PhantomReference:get()всегда возвращаетnull, ссылка нужна, чтобы черезReferenceQueueузнать о сборке объекта и освободить внешний ресурс. Удобная обёртка над нейjava.lang.ref.Cleaner.
В теории soft ссылки годятся для кэша. На Android это антипаттерн, и так прямо написано в документации к SoftReference: рантайм не знает, какие ссылки ценнее, и не может выбрать между очисткой кэша и ростом кучи. Сборщик в Android агрессивно чистит soft и weak ссылки, кэш постоянно пустеет. Рекомендация: LruCache из android.util или androidx.collection с явным размером, например восьмая часть Runtime.getRuntime().maxMemory(), и уменьшение кэша в onTrimMemory.
Для картинок кэш лучше отдать Glide или Coil: там LRU в памяти и на диске, а у Glide weak ссылки держат только ресурсы, которые сейчас на экране.
Где спотыкаются
Утверждают, что Glide или Picasso хранят картинки на soft ссылках. Обе библиотеки используют LRU с ограничением по размеру, а от soft кэша для битмапов Google отговаривает много лет.
Путают soft и weak: говорят, что weak живёт до нехватки памяти. Наоборот, weak уходит при первой же сборке без сильных ссылок, а soft держится дольше.
Забывают, что get() может вернуть null в любой момент, и пишут ref.get().decode(). Значение сохраняют в локальную переменную и проверяют на null, иначе между проверкой и вызовом объект успеет исчезнуть.