title: Using SoftReference to cache Bitmap on Android cause OOM
body: <p>I'm developing an application that need to load Bitmap. And using a <code>SoftReference</code> for the cache. I relate every soft reference with a <code>ReferenceQueue</code> and using a hash map to access the <code>SoftReference</code> . As following shows:</p>
  
  <pre><code>public static class MemCache {
  
      final private ReferenceQueue&lt;Bitmap&gt; queue = new ReferenceQueue&lt;Bitmap&gt;();
      private Map&lt;String, SoftReference&lt;Bitmap&gt;&gt; hash = null;
  
      public MemCache() {
          hash = Collections.synchronizedMap(
              new LinkedHashMap&lt;String, SoftReference&lt;Bitmap&gt;&gt;()
          );
      }
  
      public synchronized Bitmap put(String key, Bitmap value) {
          clean();
          SoftReference&lt;Bitmap&gt; ref = new SoftReference&lt;Bitmap&gt;(value, queue);
          SoftReference&lt;Bitmap&gt; res = hash.put(key, ref);
          if (res == null) return null;
          return res.get();
      }
  
      public synchronized Bitmap get(Object key) {
          clean();
          SoftReference&lt;Bitmap&gt; ref = hash.get(key);
          if (ref == null) return null;
          Bitmap val = ref.get();
          if (val != null) return val;
          hash.remove(key);
          return null;
      }
  }
  </code></pre>
  
  <p>Then, when I write the <code>clean()</code> like :</p>
  
  <pre><code>    private synchronized void clean() {
          Reference&lt;? extends Bitmap&gt; sv;
          while ((sv = queue.poll()) != null)
              hash.remove(sv);
          Queue&lt;String&gt; toRemove = new LinkedList&lt;String&gt;();
          for(Entry&lt;String, SoftReference&lt;Bitmap&gt;&gt; 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", "="));
      }
  </code></pre>
  
  <p>Which check all the SoftReferences in the hash table for not null.
  The memcache appears good, but if I only wrote:</p>
  
  <pre><code>    private synchronized void clean() {
          Reference&lt;? extends Bitmap&gt; sv;
          while ((sv = queue.poll()) != null)
              hash.remove(sv);
          Log.e("ImageCacheManager", "MemCache Size/2:" + new String(new char[hash.size() / 2]).replace("\0", "="));
      }
  </code></pre>
  
  <p>Which only remove the element been put into ReferenceQueue
  The Log then will print more and more <code>=</code>, even there is some decrease, the tendency is increase</p>
  
  <p>As what is mentioned in <a href="http://www.ibm.com/developerworks/library/j-refs/" rel="nofollow">http://www.ibm.com/developerworks/library/j-refs/</a> </p>
  
  <p>The <code>referent</code> of the <code>SoftReference</code> is set to null. But most of the <code>SoftReference</code> was not in <code>ReferenceQueue</code>. 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?</p>