Soft reference does not behave as expected on Andr

2019-08-14 17:33发布

I encountered a strange problem when I was using soft reference on Android. I implemented a class for bitmap cache, the source code is as follows:

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;
    }
}

But I found through the logcat that soft reference is collected frequently. The log is:

#######################soft ref was collected!!!

As far as I know, soft reference will be collected by GC only if java heap grow to its limit and there is no space for a new memory allocation.

But why soft reference on Android does not behave as expected?

1条回答
你好瞎i
2楼-- · 2019-08-14 17:57

As far as I know, soft reference will be collected by GC only if java heap grow to its limit and there is no space for a new memory allocation.

This is incorrect.

According to Oracle documentation, any given SoftReference may be collected anytime, if the GC makes decision to do so. There is even VM parameter, called -XX:SoftRefLRUPolicyMSPerMB. So SoftReferences are meant to get cleared before having to increase heap size even on desktop JVMs (see also this question for some extra details on the matter).

Android documentation gives even less guarantees, and clearly warns, that the VM won't actually insist on keeping those references around for long:

Unlike a WeakReference, a SoftReference will not be cleared and enqueued until the runtime must reclaim memory to satisfy an allocation.

Which I personally read as "until next GC_FOR_ALLOC".

There are certainly some valid uses for SoftReferences, such as making them a circuit breaker. The linked article also explains, why caching is not one of them. If you want to manage your cache in manner, that actually matters, use memory-limited LruCache and clear that from onLowMemory(). Or, better yet, just let go of Bitmaps after using them and let the OS decide, what to cache and when to destroy your application and it's caches.

查看更多
登录 后发表回答