WeakReferences and typical use cases

I am sure every Java programmer albeit a bit advanced would have definitely come across the term “WeakReference”. After hearing this majority of them would have read the Javadoc, which says,

Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.

Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.

Where the weakly reachable is explained by the below set of rules on reachability,

  • An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.
  • An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
  • An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.

So apparently not very clear, this is what I felt when I read it the first time. So this warrants an example at the minimum and that is what I am going to try and post below. 

Typically you would start to think that how would a resource that is already gone, what would you do with that? So as the quote above mentioned that it is used to implement canonicalizing mappings, you would want to handle resource that this depend or are mapped against this resource that just been collected. The most common way of doing this is, subclassing the WeakReference class as I have done for my sample below,

class WeakResource extends WeakReference
{
String mappingToHandle;

public WeakResource(Object weakRes, ReferenceQueue queue, String mapping)
{
super(weakRes,queue);
mappingToHandle = mapping;
}

public void close()
{
System.out.println("Closing the mapping: "+mappingToHandle);
mappingToHandle = null;
}
}

In the above segment in the constructor the referrent and the ReferenceQueue instance are passed to the super class constructor. ReferenceQueue instance is an object where collections show up after the garbage collector has applied the reachability rules. In our case an instance of the object WeakResource would show up after it is deemed weakly reachable. It is important to note that the close method is the one that is used to close the mapped resources or clean up the canonicalizing mappings as the Javadoc mentions.

Next is the reaper thread that needs to pick up things off the RefereceQueue and clean up the mappings, 

class ReferenceReaper extends Thread
{
private ReferenceQueue queueToPoll;

public ReferenceReaper(ReferenceQueue queue)
{
super("RefReaper Thread");
queueToPoll = queue;
setDaemon(true);
}

public void run()
{
WeakResource resource;
while (true)
{
System.out.println("Running");
while ((resource = (WeakResource) queueToPoll.poll()) != null)
{
System.out.println("Found resource");
System.out.println("Mapping is: " + resource.mappingToHandle);
resource.close();
}
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
// Ignore for sample program
}
}
}
}

In the above code, the reaper is created as a daemon thread looking at appearances of WeakResource objects on the reference queue. It wakes up at regular intervals and calls the poll() method on the reference queue whose javadoc is like below,

Polls this queue to see if a reference object is available. If one is available without further delay then it is removed from the queue and returned. Otherwise this method immediately returns null.

So when this reaper thread runs it will retrieve all items that show up in the queue and call close to clean up the mapped resources

Finally, putting it all together. 

public class WeakRefImpl
{
private List<WeakResource> weakResources;
private ReferenceQueue queue;

public WeakRefImpl()
{
weakResources = new ArrayList<WeakResource>();
queue = new ReferenceQueue();
new ReferenceReaper(queue).start();
}

public void createResources()
{
Object []resources = new Object[]{new Object(),new Object(),new Object(),new Object(), new Object()};
int i = 0;
String mapping = "map - ";
for(Object res : resources)
{
WeakResource weakRes = new WeakResource(res,queue,mapping + i);
weakResources.add(weakRes);
i++;
}
resources = null;
}

public static void main(String []args) throws Exception
{
WeakRefImpl impl = new WeakRefImpl();
impl.createResources();
//Try gc'ing. This is only for sample, usually no one should call this and just let JVM do its job and then gather the references
System.gc();
Thread.sleep(1000*1000);
}

}

In the above segment, in the createResources method the WeakResource object is created and added to a list. Then right at the end of the method the resources array is nullified which makes is eligible for garbage collection.

FInally in the main method call the createResources method and the explicitly call the GC for demo purposes. Ideally this will never be done and when the GC happens all the WeakResource objects will queue up at the ReferenceQueue and mapped resources would get closed. I put in a big sleep right at the end to let the program finish collecting the resources.

When all the above pieces are put together and run the output in my machine would like this,

Running
Running
Found resource
Mapping is: map - 0
Closing the mapping: map - 0
Found resource
Mapping is: map - 4
Closing the mapping: map - 4
Found resource
Mapping is: map - 3
Closing the mapping: map - 3
Found resource
Mapping is: map - 2
Closing the mapping: map - 2
Found resource
Mapping is: map - 1
Closing the mapping: map - 1

This could be different in your machine and different on my machine if I run again as the order GC puts them on the reference queue would change.

Some of the common use cases that I have used them is for implementing JDBC connection to JDBC statement mappings. All statements would need to know about connections and hence would have to have strong reference to connection, but the connection would only need to have a weak reference. That way, when the statement goes away, the connection can do the cleanup or handle the case for Statement pooling. Other use is that for determining leaks during diagnostic runs, where one can use the same concept of closing things with weak mappings when they were not explicitly closed. This would help determine leaks of un-closed resources.

Hope this example clarifies typical use cases for WeakRefernce and makes things more clear !!! 

Happy Coding 🙂