Archive for November 2007

Weak and Soft References in Java

Types of References in Java

Before I get to the point, let me introduce you a couple of classes we’ll count on when going through examples later.

The first one is Item:

package com.centuryminds.softreferences;

/**
 * An item
 */
class Item
{
  //class members
}

And here comes Group:

package com.centuryminds.softreferences;

import java.util.ArrayList;
import java.util.List;

/**
 * A group of {@link Item}
 */
public class Group<T extends Item>
{
  private final List<T> items = new ArrayList<T>();

  public void add(T item)
  {
    items.add(item);
  }

  // other methods
  // ...
}

Let’s start the ball rolling. This is the most common way to create a reference to an object from another one (okay, forget about the factories, IoC and such for a while):

Item item = new Item();

The item element is called a strong reference. Why is regarded as strong? A new well-kown player named Garbage Collector -GC from now on- comes up , which is responsible of taking care (i.e. freeing) of the memory resources. When an object Client, holding a reference to item:

A strong reference

decides to not use item anymore:

Not needed anymore

after an undetermined time, the GC might decide to dispose of the instance formerly referenced by item.

To illustrate a dime a dozen problem associated to strong references, let’s suppose we have an object Group, referencing a bunch of Item elements through its internal items list member.

Items in a Group

As we can see in the figure, there are a couple of external references to the Items. What happens when these references are not needed anymore and stop pointing at those Items?

No more external references

At this point in time, the logic of our application might require us to remove the references to the Items in our Group instance, since they are not used anymore. That’s a burden every now and then. Actually, what we are doing is duplicating efforts, since this should be the main responsibility of the GC.

WeakReferences

A WeakReference is a reference which does not prevent an object to be reclaimed by the Garbage Collector. A WeakReference allows you to pass the GC the buck when determining the reachability of an object for you.

A weak reference

Here is how a WeakReference is created:

package com.centuryminds.softreferences;

/**
 * Creating a WeakReference
 */
class A
{
  private final WeakReference<B> b = new WeakReference<B>(new B());

  public B getB()
  {
    return b.get();
  }
}

A small hint here: the method getB() might return null if there are no strong references to the internal reference b. As a matter of fact, the previous is an awful example, since the first call to getB() could return null in case the GC decided to reclaim b and release the memory assigned to it before that method call. Not good.

SoftReferences

Another far more interesting type of reference we may take advantage of is the SoftReference. In short, this type of reference is like a WeakReference, but tells the GC to not reclaim the wrapped object if there is enough free memory. In other words, if the the GC needs more free memory after reclaiming the objects reachable through strong and weak references, then it will reclaim the instances only reachable through soft references. This, as we will see, makes an excellent candidate for becoming a smart invalidation policy within a cache.

An all-dancing cache

We are going to build up a cache of objects using soft references. Motivation for a cache:

  1. There are some elements which are very expensive to retrieve
  2. Those elements are used frequently
  3. The number of such elements (or the number of the most frequently used) is limited

In our particular case, the expected behavior of our cache will be:

The objects stored in the cache will be discarded if they are referenced by the cache only AND more free memory is required.

The Garbage Collector will be in charge of that. The ball is in its court now.

A code snippet is worth a thousand words:

package com.centuryminds.softreferences;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

/**
 * Generic cache based on SoftReference's
 */
public class Cache<T>
{
  private final Map<String, SoftReference<T>> cache = new HashMap<String, SoftReference<T>>();

  /**
   * Look an element up by its id
   */
  public T get(final String id)
  {
    T elem = null;

    if (cache.containsKey(id))
    {
      SoftReference<T> ref = cache.get(id);
      elem = ref.get();
    }

    // elem could value null here in two cases:
    // (1) it was not stored in the cache
    // (2) it was garbage collected
    if (elem == null)
    {
      elem = getElemFromExternalService(id);
      cache.put(id, new SoftReference<T>(elem));
    }

    return elem;
  }
}

This cache manages elements of unknown types at compile-time, which are retrieved by their ids through calls to getElemFromExternalService(id). This method may be a private member of the cache, a callback or whatever. The key point here is that the elements are stored as soft references. When an external entity asks the cache for an element, it is linked to it through a strong reference:

// returns the wrapped object
elem = ref.get();
...
// a strong reference for the callee
return elem;

Bear in mind this is vital to our purposes, since the existence of a strong reference whilst the object is being used will prevent it from being garbage-collected.

Extra goodies

  • WeakHashMap is a Map whose keys are stored using weak references. Refer to the Javadocs for further information.
  • A wrapped object by a WeakReference becoming rubbish does not imply that the WeakReference itself defuncts. We thus have to perform some kind of cleanup of those references. WeakReference has got a contructor which accepts a ReferenceQueue, which is used to track stale references to WeakReferences objects. Take a look at the Javadocs to get more information on how to use it.
  • There is another type reference named phantom reference, but it is not worth the hassle explaining it for the purposes of this post.
  • An open question: if WeakHashMap exists, what happened to SoftHashMap? I don’t want to make a mountain out of a molehill, but this class would be at least as useful as WeakHashMap, if not more.