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:

decides to not use item 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.

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?

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.

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:
- There are some elements which are very expensive to retrieve
- Those elements are used frequently
- 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
Mapwhose keys are stored using weak references. Refer to the Javadocs for further information. - A wrapped object by a
WeakReferencebecoming rubbish does not imply that theWeakReferenceitself defuncts. We thus have to perform some kind of cleanup of those references.WeakReferencehas got a contructor which accepts a ReferenceQueue, which is used to track stale references toWeakReferences 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
WeakHashMapexists, what happened toSoftHashMap? I don’t want to make a mountain out of a molehill, but this class would be at least as useful asWeakHashMap, if not more.