Мягкая ссылка не работает должным образом на Android
Я столкнулся со странной проблемой, когда использовал Android на Android. Я реализовал класс для растрового кэша, исходный код которого выглядит следующим образом:
public class ImageCache
{
private static HashMap<String, SoftReference<Bitmap>> mCache = new HashMap<String, SoftReference<Bitmap>>();
private static final String TAG = "ImageCache";
public static Bitmap getBitmap(String url)
{
Bitmap bitmap = null;
if (mCache.containsKey(url))
{
Log.d(TAG, "use cache: " + url);
bitmap = mCache.get(url).get();
if (bitmap != null)
{
return bitmap;
}
else
{
Log.w(TAG, "#######################soft ref was collected!!!");
}
}
bitmap = BitmapFactory.decodeFile(url);
if (bitmap == null)
{
Log.e(TAG, "#####jpg not found");
return null;
}
bitmap = Bitmap.createScaledBitmap(bitmap, 320, 240, false);
synchronized (mCache) {
mCache.put(url, new SoftReference<Bitmap>(bitmap));
}
return bitmap;
}
}
Но через logcat я обнаружил, что мягкие ссылки часто собираются. Журнал это:
#######################soft ref was collected!!!
Насколько я знаю, мягкие ссылки будут собираться GC только в том случае, если куча java увеличится до своего предела и не будет места для нового выделения памяти.
Но почему мягкая ссылка на Android не ведет себя так, как ожидалось?
1 ответ
Насколько я знаю, мягкие ссылки будут собираться GC только в том случае, если куча java увеличится до своего предела и не будет места для нового выделения памяти.
Это неверно
Согласно документации Oracle, любая данная SoftReference может быть собрана в любое время, если GC примет решение сделать это. Есть даже параметр VM, называемый -XX:SoftRefLRUPolicyMSPerMB
, Таким образом, SoftReferences предназначены для очистки перед тем, как увеличивать размер кучи даже на настольных JVM (см. Также этот вопрос для получения дополнительной информации по этому вопросу).
Документация по Android дает еще меньше гарантий и четко предупреждает, что виртуальная машина не будет настаивать на сохранении этих ссылок долго:
В отличие от WeakReference, SoftReference не будет очищаться и ставиться в очередь до тех пор, пока среда выполнения не освободит память для удовлетворения выделения.
Который я лично читаю как "до следующего GC_FOR_ALLOC
".
Есть, конечно, некоторые допустимые применения для SoftReferences, например, чтобы сделать их автоматическим выключателем. Связанная статья также объясняет, почему кэширование не является одним из них. Если вы хотите управлять своим кэшем таким образом, который действительно имеет значение, используйте ограниченную память LruCache
и ясно, что из onLowMemory()
, Или, что еще лучше, просто отпустите растровые изображения после их использования и дайте ОС решить, что и когда кэшировать, а также уничтожать ваше приложение и его кэши.