软引用在 Android 上的行为不符合预期
Soft reference does not behave as expected on Android
我在Android上使用软引用时遇到了一个奇怪的问题。
我实现了一个class用于位图缓存,源码如下:
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 堆增长到极限并且没有 space 用于新内存分配时,GC 才会收集软引用。
但为什么 Android 上的软引用没有按预期运行?
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.
这是不正确的。
根据 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 有关此事的一些额外细节)。
Android documentation gives even less guarantees, and clearly warns,虚拟机实际上不会坚持长时间保留这些引用:
Unlike a WeakReference, a SoftReference will not be cleared and enqueued until the runtime must reclaim memory to satisfy an allocation.
我个人认为是 "until next GC_FOR_ALLOC
"。
SoftReferences 肯定有一些有效的用途,例如使它们成为 circuit breaker。链接的文章还解释了为什么缓存不是其中之一。如果您想以某种方式管理您的缓存,那实际上很重要,请使用内存限制 LruCache
并从 onLowMemory()
中清除它。或者,更好的是,在使用它们之后放开位图,让 OS 决定缓存什么以及何时销毁您的应用程序及其缓存。
我在Android上使用软引用时遇到了一个奇怪的问题。 我实现了一个class用于位图缓存,源码如下:
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 堆增长到极限并且没有 space 用于新内存分配时,GC 才会收集软引用。
但为什么 Android 上的软引用没有按预期运行?
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.
这是不正确的。
根据 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 有关此事的一些额外细节)。
Android documentation gives even less guarantees, and clearly warns,虚拟机实际上不会坚持长时间保留这些引用:
Unlike a WeakReference, a SoftReference will not be cleared and enqueued until the runtime must reclaim memory to satisfy an allocation.
我个人认为是 "until next GC_FOR_ALLOC
"。
SoftReferences 肯定有一些有效的用途,例如使它们成为 circuit breaker。链接的文章还解释了为什么缓存不是其中之一。如果您想以某种方式管理您的缓存,那实际上很重要,请使用内存限制 LruCache
并从 onLowMemory()
中清除它。或者,更好的是,在使用它们之后放开位图,让 OS 决定缓存什么以及何时销毁您的应用程序及其缓存。