ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button Java Design and Performance Optimization

The WeakHashMap Class

07/09/2001

WeakHashMap is a type of Map which differs from other Maps in more than just having a different implementation.

WeakHashMap uses weak references to hold its keys, making it one of the few classes able to respond to the fluctuating memory requirements of the JVM. This can make WeakHashMap unpredictable at times, unless you know exactly what you are doing with it. In the following sections I examine how best to use WeakHashMap, and why WeakHashMap behaves the way it does.

Using WeakHashMap to Avoid Memory Leaks

How many times have you used a Map to store extra information about objects? Perhaps subclassing of those objects were disallowed, but you needed to record extra state information for each object; or there were many different types of objects, and you needed to have a central repository where information was easily accessible about any one of the objects.

Here's a simple example of a class which allows any object to be registered with it, and which keeps track of two items of information about that object, the elapsed time since the object was registered, and a separate counter.


public class ExtraInformation
{
  static Map ExtraInfo = new HashMap();

  long registrationTime;
  int countSomething = 0;

  public static void registerObject(Object o)
  {
    ExtraInfo.put(o, new ExtraInformation())
  }

  public ExtraInformation()
  {
    //record time when first registered
    registrationTime = System.currentTimeMillis();
  }

  public static void incrementCount(Object o)
  {
    ((ExtraInformation) ExtraInfo.get(o)).incrementCount++;
  }

  public static int getCount(Object o)
  {
    return ((ExtraInformation) ExtraInfo.get(o)).incrementCount;
  }

  public static long getElapseTimeMillis(Object o)
  {
    return System.currentTimeMillis() - 
      ((ExtraInformation) ExtraInfo.get(o)).registrationTime;
  }
}

Related Reading

Java Performance TuningJava Performance Tuning
By Jack Shirazi
Table of Contents
Index
Sample Chapter
Full Description
Read Online -- Safari

If you are alert, or thinking about the title of this section, you may have immediately noticed that a deregistration method is missing. Without a deregistration method, the internal Map in the ExtraInformation class keeps on growing, and retains a reference to the object key. This is a classic memory leak; objects never get dereferenced, so the garbage collector can never reclaim them. So lets add the deregistration method:

public static void deregisterObject(Object o)
{
  ExtraInfo.remove(o);
}

Now we can avoid the memory leak. But what if the users of this class forget to call the deregister method? What if there is no particular time when it would be appropriate to call the deregister method? Well, we can put a big warning in the documentation -- "WARNING: MEMORY LEAK IF DEREGISTRATION IS NOT PERFORMED!". Or we can try to be nicer and handle the situation ourselves.

Specifically, we want to handle the situation where the deregisterObject() method is not called, but the registered object is no longer referenced anywhere in the application, except by our internal Map. This situation is precisely what WeakHashMap was designed for. The keys entered into a WeakHashMap are held using WeakReferences. This allows the garbage collector to collect the keys if there are no other strong references to those keys elsewhere in the application (for more details about strong and weak references in Java, see the "Further resources" section at the end of this article). After the keys have been collected, the values are also dereferenced from the WeakHashMap, and can also be garbage collected if they are not referred to elsewhere in the application.

To enable our ExtraInformation class to work with the garbage collector in avoiding memory leaks, the change is simple -- make the internal Map a WeakHashMap:

static Map ExtraInfo = new WeakHashMap();

Comment on this articleSend your questions to Jack.
Post your comments

Also in Java Design and Performance Optimization:

Micro-Tuning Step-by-Step

Tuning JDBC: Measuring JDBC performance

Faster List Iteration with RandomAccess Interface

With this one change, we have now enabled our ExtraInformation class to automatically release objects to the garbage collector once those objects are no longer referenced anywhere else in the application. (There can be other classes using weak references in a similar way without interfering with this functionality. No amount of weak references to an object will prevent that object from being eligible for collection by the garbage collector).

Using WeakHashMap as a Cache

The previous section looked at using WeakHashMap to avoid memory leaks. WeakHashMap can also be used fairly straightforwardly as a cache that can be cleared automatically when JVM memory is low. The only difference is in needing the keys to be objects which can be equal to other keys of the same class. This is most easily explained with some examples:

  • Unique keys. If the keys were of the Object class, each key would be unique and could not evaluate equal to another key. Consequently, once you no longer retained a reference to the Object key, there would be no way of re-accessing values, short of enumerating all keys or values (this is true for any Map). Since a cache normally works by allowing values to be re-accessed, this would be a severe problem.

  • Equal Keys. If the keys are objects like Strings or Integers, then any particular key value could be reaccessed by creating a new key which evaluates as equal (according to the equal() method for that class) to the original key. This allows the original key to have no further strong references to it, thus enabling it to be sucessfully used as a WeakHashMap key.

    For example, a cache with the functionality of a sparse array (see the "Sparse Arrays" sidebar) is easy to implement:

public class SparseArrayCache {
  protected WeakHashMap map = new WeakHashMap();

  /* This put() will return any previous value already at index i. */
  public Object put(int i, Object o) {
    map.put(new Integer(i), o);
  }

  /* This get() will return the object at index i, or null if no
   object was put there, or if the object was garbage collected. */
  public Object get(int i) {
    return map.get(new Integer(i));
  }

  public void remove(int i, Object o) {
    map.put(new Integer(i), o);
  }
}

Pages: 1, 2

Next Pagearrow