Java provides a way for the programmer to exercise control over memory management by marking certain objects as expendable via reference objects. Should an application’s memory requirements quickly increase, the garbage collector is free to collect any objects that can only be reached through reference objects—also known as weak references—and free the memory they occupy.
In my previous article, I described how to use Java’s reference objects, SoftReference and WeakReference, to build weak references and mark blocks as reclaimable in a simple application. Now let’s take a look at a slightly more useful example that shows how reference objects can be used to provide simple but effective caching functionality to an application.
For more information
In addition to reading my previous article, I highly recommend reading more on Java’s garbage collection process. Check out “Reference Objects and Garbage Collection” on Sun’s Java Web site.
Reference queues defined
A reference object evaluates to null when the weak reference it holds is cleared. But what if we could remove weak references automatically as they are cleared? The Java Platform provides support for this capability in the form of reference queues: instances of the java.lang.ref.ReferenceQueue class. When a weak reference is created, it can be registered with a reference queue so that when the weak reference is cleared (i.e., when the object it refers to is about to be garbage-collected), it is added to the reference queue.
The reference queue provides methods, both blocking and nonblocking, to remove weak references from the queue. You can use a number of strategies, such as polling the queue at key points of the program or using a dedicated thread, to traverse the queue and do any necessary cleanup.
Listing A contains my familiar memory consumption class, MemoryBlock, while Listing B presents a sample application that consumes ever-larger MemoryBlocks that are held through reference objects, so that their memory can be freed when needed. The sample application makes use of a custom reference class, MyReference, found in Listing C, which descends from SoftReference.
As you can see, each MyReference object is registered with a reference queue as it is created. As MemoryBlocks are garbage-collected, their corresponding reference objects are added to the reference queue. Before printing the ArrayList, I traverse the queue and remove the null references from it.
Figure A shows the output of the example. Notice that the array contains only the reference objects that point to valid MemoryBlocks; those that have been reclaimed by the garbage collector have been removed.
|Results of running MemoryTest3|
Easy caching with reference objects
One of the most common practical applications of reference objects is in-memory caches, where instead of saving the actual object to the cache, you save a weak reference to it. The garbage collector can reclaim the memory used by the cached objects as needed, and you can keep track of which objects have been reclaimed using a reference queue. The example in Listing A could easily be rewritten to work as a cache in this fashion.
However, the Java Platform provides a data structure that greatly simplifies the use of reference objects like this, called the java.util.WeakHashMap class. WeakHashMap works exactly like a hash table but uses weak references internally. In practice, entries in the WeakHashMap are reclaimed as needed to make free memory. In Listing D, you’ll find the code for a very simple WebObject cache that could be used, for example, in a Web browser.
The cache provides a get method that receives a URL and returns a Web object containing the content found at that URL. The objects are saved in a WeakHashMap so that if they are requested again shortly, chances are they are still in the hash table, and the saved copy is returned rather than downloading the content once again. If the object doesn’t exist in the cache (i.e., either because it hasn’t been requested before or the garbage collector removed it), the class gets the object from the URL and saves a weak reference in the WeakHashMap before returning it.