【问题标题】:Using SoftReference to cache Bitmap on Android cause OOMAndroid上使用SoftReference缓存Bitmap导致OOM
【发布时间】:2012-01-03 15:32:17
【问题描述】:

我正在开发一个需要加载位图的应用程序。并使用SoftReference 作为缓存。我将每个软引用与 ReferenceQueue 联系起来,并使用哈希映射来访问 SoftReference 。如下图:

public static class MemCache {

    final private ReferenceQueue<Bitmap> queue = new ReferenceQueue<Bitmap>();
    private Map<String, SoftReference<Bitmap>> hash = null;

    public MemCache() {
        hash = Collections.synchronizedMap(
            new LinkedHashMap<String, SoftReference<Bitmap>>()
        );
    }

    public synchronized Bitmap put(String key, Bitmap value) {
        clean();
        SoftReference<Bitmap> ref = new SoftReference<Bitmap>(value, queue);
        SoftReference<Bitmap> res = hash.put(key, ref);
        if (res == null) return null;
        return res.get();
    }

    public synchronized Bitmap get(Object key) {
        clean();
        SoftReference<Bitmap> ref = hash.get(key);
        if (ref == null) return null;
        Bitmap val = ref.get();
        if (val != null) return val;
        hash.remove(key);
        return null;
    }
}

然后,当我写clean() 之类的:

    private synchronized void clean() {
        Reference<? extends Bitmap> sv;
        while ((sv = queue.poll()) != null)
            hash.remove(sv);
        Queue<String> toRemove = new LinkedList<String>();
        for(Entry<String, SoftReference<Bitmap>> e : hash.entrySet()){
            if(e.getValue()==null) continue;
            if(e.getValue().get()==null) 
                toRemove.add(e.getKey());
        }
        String s;
        while((s = toRemove.poll())!= null)
            hash.remove(s);
        Log.e("ImageCacheManager", "MemCache Size/2:" + new String(new char[hash.size() / 2]).replace("\0", "="));
    }

检查哈希表中的所有 SoftReferences 是否不为空。 内存缓存看起来不错,但如果我只写:

    private synchronized void clean() {
        Reference<? extends Bitmap> sv;
        while ((sv = queue.poll()) != null)
            hash.remove(sv);
        Log.e("ImageCacheManager", "MemCache Size/2:" + new String(new char[hash.size() / 2]).replace("\0", "="));
    }

其中只移除被放入ReferenceQueue的元素 然后Log会打印越来越多=,虽然有一些减少,但有增加的趋势

正如http://www.ibm.com/developerworks/library/j-refs/中提到的那样

SoftReferencereferent 设置为空。但大部分SoftReference 不在ReferenceQueue 中。只是在对象被标记为可终结但未最终确定的状态之间? 标记为 finalizable 但未最终确定的 Bitmap 会被回收吗?

【问题讨论】:

  • 会不会是linkedhashmap?我可能弄错了,但是因为映射的键不是软引用,所以 JVM 不会释放内存。它是指软引用的硬引用吗?
  • 如果没有对位图的强引用,GC 会将 SoftReference 的引用设置为 null,则不会期望收集到它自己的 SoftReference 对象。我认为这可能不是 Bitmap Object 的内存没有被回收的原因

标签: java android garbage-collection out-of-memory soft-references


【解决方案1】:

我已经尝试过类似的问题。一段时间后,我意识到这是因为 android 管理位图的方式。如果我没有误解,他们使用“skia”,一种本机实现,用于位图。所以位图不是在Java堆中分配的,而是在本机堆中分配的,Java位图对象本身非常小,不适合GC。因此他们提供了释放位图保留的本机内存的回收方法。

对不起,我的英语很差。

【讨论】:

  • = =... 所以...这是因为 VM 堆上的位图对象的大小很差。那么,有什么方法可以让缓存工作吗?
  • 其实要看需求。我在内存中保留了少量位图(例如,更频繁地访问的位图)并从 sdcard 加载其他位图。但是你总是可以在catch子句中捕获OOM异常并释放本机内存,但正如我已经说过的,这取决于你的要求......没有确切或正确的解决方案。
  • 我还发现计算出来的标记为finalizable但没有被回收的图像已经达到了75MB的大小!这是否意味着应用程序可以分配的本机堆大小不受 16MB 虚拟机堆大小或 24MB 的限制?
  • Er... 在解码 Bitmap 时使用 Bitmap.Options 并分配 inTempStorage 怎么样? “skia”会使用java代码分配的内存吗?
  • 不,据我所知,skia 使用本机内存。 inTempoStarage 允许您更改用于解码文件的缓冲区。我从未使用过它,但默认尺寸是 16 K,所以我不认为这是这个缓冲区的问题。您是如何计算图像大小的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多