1

在 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!!!

据我所知,只有当java堆增长到极限并且没有空间用于新的内存分配时,GC才会收集软引用。

但是,为什么 Android 上的软引用不符合预期呢?

4

1 回答 1

6

据我所知,只有当java堆增长到极限并且没有空间用于新的内存分配时,GC才会收集软引用。

这是不正确的。

根据 Oracle文档,如果 GC 决定这样做,则可以随时收集任何给定的 SoftReference。甚至还有一个 VM 参数,称为-XX:SoftRefLRUPolicyMSPerMB. 因此,即使在桌面 JVM 上也必须在增加堆大小之前清除SoftReferences (有关此问题的更多详细信息,请参阅此问题)。

Android文档提供的保证更少,并且明确警告说,VM 实际上不会坚持长期保留这些引用:

与 WeakReference 不同的是,在运行时必须回收内存以满足分配之前,SoftReference 不会被清除和排队。

我个人将其解读为“直到下一个GC_FOR_ALLOC”。

SoftReferences 肯定有一些有效的用途,例如使它们成为断路器。链接的文章还解释了为什么缓存不是其中之一。如果您想以某种方式管理缓存,这实际上很重要,请使用内存限制LruCache并从onLowMemory(). 或者,更好的是,在使用位图后放开它们,让操作系统决定要缓存的内容以及何时销毁您的应用程序及其缓存。

于 2015-07-20T09:12:02.857 回答