title: Using MapMaker to create a cache
body: <p>I want to use MapMaker to create a map that caches large objects,
  which should be removed from the cache if there is not enough memory.
  This little demo program seems to work fine:</p>
  
  <pre><code>public class TestValue {
      private final int id;
      private final int[] data = new int[100000];
  
      public TestValue(int id) {
          this.id = id;
      }
  
      @Override
      protected void finalize() throws Throwable {
          super.finalize();
          System.out.println("finalized");
      }  
  }  
  
  
  public class Main {
  
      private ConcurrentMap&lt;Integer, TestValue&gt; cache;
      MemoryMXBean memoryBean;
  
      public Main() {
          cache = new MapMaker()
                  .weakKeys()
                  .softValues()
                  .makeMap();
          memoryBean = ManagementFactory.getMemoryMXBean();
      }
  
      public void test() {
          int i = 0;
          while (true) {
              System.out.println("Etntries: " + cache.size() + " heap: "  
                  + memoryBean.getHeapMemoryUsage() + " non-heap: "  
                  + memoryBean.getNonHeapMemoryUsage());
              for (int j = 0; j &lt; 10; j++) {
                  i++;
                  TestValue t = new TestValue(i);
                  cache.put(i, t);
              }
              try {
                  Thread.sleep(100);
              } catch (InterruptedException ex) {
              }
         }
      }
  
      /**
       * @param args the command line arguments
       */
      public static void main(String[] args) {
          Main m = new Main();
          m.test();
      }
  
  }
  </code></pre>
  
  <p>However, when I do the same thing in my real application, entries are
  basically removed from the cache as soon as they are added. In my real
  application, I also use integers as keys, and the cached values are
  archive blocks read from disk that contains some data. As far as I
  understand, weak-references are garbage-collected as soon as they are
  no longer used, so this seems to make sense because the keys are weak
  references. If I create the map like this:</p>
  
  <pre><code>    data = new MapMaker()
              .softValues()
              .makeMap();
  </code></pre>
  
  <p>The entries are never garbage-collected and I get an out-of-memory
  error in my test program. The finalize method on the TestValue entries
  is never called. If I change the test method to the following:</p>
  
  <pre><code>public void test() {
      int i = 0;
      while (true) {
          for (final Entry&lt;Integer, TestValue&gt; entry :
              data.entrySet()) {
              if (entry.getValue() == null) {
                  data.remove(entry.getKey());
              }
          }
          System.out.println("Etntries: " + data.size() + " heap: "
              + memoryBean.getHeapMemoryUsage() + " non-heap: "  
              + memoryBean.getNonHeapMemoryUsage());
          for (int j = 0; j &lt; 10; j++) {
              i++;
              TestValue t = new TestValue(i);
              data.put(i, t);
          }
          try {
              Thread.sleep(100);
          } catch (InterruptedException ex) {
          }
      }
  }
  </code></pre>
  
  <p>entries are removed from the cache and the finalizer on the TestValue
  objects is called, but after a while I also get an out-of-memory
  error.</p>
  
  <p>So my question is: what is the right way to use MapMaker to create a
  map that can be used as a cache? Why does my test program not remove
  the entries as soon as possible if I use weakKeys?  Is it possible to
  add a reference queue to the cache map? </p>