1

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?

Colin Macleod
  • 4,222
  • 18
  • 21
dragonfly
  • 1,151
  • 14
  • 35
  • reference count for bitmap may be 0 .. I think. ("soft" refercence is stored in mCache but I cannot find other reference to the bitmap) – Toris Jul 20 '15 at 06:42
  • It's not recommended using SoftReference for cache. Use an LRUCache instead. – songchenwen Jul 20 '15 at 06:49

1 Answers1

8

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.

user1643723
  • 4,109
  • 1
  • 24
  • 48
  • 1
    If soft reference is nearly the same with weak reference, what is the purpose of defining 2 references almost the same? – dragonfly Jul 22 '15 at 11:29
  • 1
    @dragonfly I have clarified that part in the answer. Basically, soft references won't be reaped at least until next *stop-the-world* (also known as *"collect some garbage to avoid increasing heap size"*) GC, while weak ones may be erased during any other GC (such as a routine parallel GC). Or at least, that's how that part of documentation sounds for me. – user1643723 Jul 22 '15 at 13:16
  • I think the "circuit breaker" application for SoftReferences (keeping only a SoftReference to data you are working on to avoid OutOfMemoryErrors) is not terribly good. A, it is incomplete, as the article itself states (during certain times a strong reference will be held, defeating the purpose). B, it may lead to operations failing if the JVM just happens to be eager to throw out some data at some point - there are simply no guarantees about the lifetime of SoftReferences. I am still to find a single good use for SoftReferences. Manage your caches yourself, explicitly, with strong references. – Stefan Reich Sep 22 '19 at 10:52