2

I'm developing an application that need to load Bitmap. And using a SoftReference for the cache. I relate every soft reference with a ReferenceQueue and using a hash map to access the SoftReference . As following shows:

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

Then, when I write the clean() like :

    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", "="));
    }

Which check all the SoftReferences in the hash table for not null. The memcache appears good, but if I only wrote:

    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", "="));
    }

Which only remove the element been put into ReferenceQueue The Log then will print more and more =, even there is some decrease, the tendency is increase

As what is mentioned in http://www.ibm.com/developerworks/library/j-refs/

The referent of the SoftReference is set to null. But most of the SoftReference was not in ReferenceQueue. Just between the state that the object is marked as finalizable but not be finalized? Will the Bitmap marked as finalizable but not be finalized been recycled?

2
  • Could it be the linkedhashmap? I could be mistaken but because the key of the map isn't a softreference then the JVM will not release the memory. Has it is a hard reference referring to a soft reference. Commented Nov 22, 2011 at 8:28
  • The GC will set the referent of the SoftReference<Bitmap> to null if there no any strong reference to the bitmap, the SoftReference Object it self would not be expected to be collected. I don't think it cound be the reason that the memory of Bitmap Object not be reclaimed Commented Nov 22, 2011 at 8:36

1 Answer 1

5

I have experimented a similar issue. After sometime I had realized that is because of the way android manage bitmap. If I have not misunderstood they use "skia", a native implementation, for bitmap. So bitmaps are not allocated in the java heap but in the native heap and the java bitmap object itself is really small and a poor candidate for GC. So they provided the recycle method that free the native memory retained by a bitmap.

Sorry for my poor english.

Sign up to request clarification or add additional context in comments.

6 Comments

= =... So... it is because the poor size of the Bitmap Object on the VM Heap. Then, Are there any available method to have the cache work?
Actually depends on the requirements. I keep in memory a small amount of bitmaps (the bitmaps accessed more often, for instance) and load the others from the sdcard. But you can always catch the OOM exception and free the native memory in the catch clause, but as I have already said, it depends on your requirements... There is not an exact or correct solution.
I've also found that the calculated image that marked as finalizable but not be recycled have reach a size of 75MB! Does this mean that the native heap size which application can allocate would not be limited by the 16MB VM Heap size or 24MB?
Er... How about using a Bitmap.Options and allocate inTempStorage when decode the Bitmap? Will the "skia" use the memory allocated by java code?
nope skia use native memory as far as i know. inTempoStarage allow you to change the buffer used to decode file. I have never used it but the default dimension is 16 K, so I do not think it is this buffer the problem. How do you calculated the image size?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.