Reviewing the WeakHashMap
The WeakHashMap is a Java collection that uses the WeakReference class to hold its keys. As we've already seen, weak references to an object are cleared by the garbage collector as soon as there are no strong or soft references to the same object. The result is that entries only remain in the WeakHashMap as long as there are references to the map keys lying around your JVM.
Here is an example:
import java.util.WeakHashMap;
public class WeakHashMapSample1 {
public static void main(String[] args) {
WeakHashMap weakHashMap = new WeakHashMap();
// Create a key for the map, but keep the strong reference
String keyStrongReference = new String("key");
weakHashMap.put(keyStrongReference, "value");
// Run the GC and check if the key is still there.
System.gc();
System.out.println(weakHashMap.get("key"));
// Now, null-out the strong reference and try again.
keyStrongReference = null;
System.gc();
System.out.println(weakHashMap.get("key"));
}
}
The code above prints:
value
null
What happened here? At the first time we called System.gc(), there was still a reference to the key in the
keyStrongReference class. Because of this, the map key was not cleared by the garbage collector. In the following line, we got rid of the strong reference to the key and tried again. This time, the call to weakHashMap.get("key") returned null.Implementing a Weak Object Pool with a WeakHashMap
If you make heavy use of small immutable classes, like String and the primitive wrapper classes like java.lang.Integer, you can take advantage of the WeakHashMap behavior to share instances and reduce memory usage, while at the same time not having to worry about objects lingering in memory after they are no longer memory. The real benefit depends on how many instances you create and how the values are distributed, but you can potentially reduce the number of objects created by several orders of magnitude.
Here is an example of a weak object pool:
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Oversimplistic implementation of an object pool
*/
public class WeakObjectPool {
// Map where the key is an object, and the value is a weak reference
// to the same object. We use the key to do the lookup, and the value
// to actually return the object when it is found.
private Map map = new WeakHashMap();
public Object replace(Object object) {
WeakReference reference = (WeakReference) map.get(object);
if (reference != null) {
Object result = reference.get();
// Another null check, since the GC may have kicked in between the
// two lines above.
if (result != null) {
return result;
}
}
// If we got here it is because the map doesn't have the key, add it.
map.put(object, new WeakReference(object));
return object;
}
}
Now, this class can be used like this:
class ObjectPoolClient {
private static WeakObjectPool objectPool = new WeakObjectPool();
public static void main(String args[]) throws Exception {
BufferedReader reader = new BufferedReader(new FileReader("input.csv"));
List<String[]> parsedLines = new ArrayList<String[]>();
String line;
while ((line = reader.readLine()) != null) {
String[] elements = line.split(",");
for (int i = 0; i < elements.length; i++) {
// replace the string read from the file with the pool instance
elements[i] = (String) objectPool.replace(elements[i]);
}
parsedLines.add(elements);
}
reader.close();
// Cool, we saved a lot of memory by reusing the repeated strings!
doSomethingInteresting(parsedLines);
// Now, we get rid of the references and soon the garbage collector
// will reclaim the memory
parsedLines = null;
doMoreInterestingStuff();
}
}Assuming the input file contains lots of repeated values, we've been able to save a lot of heap space by not having the same string repeated over and over again in memory. Also, we get the added benefit of releasing the memory when the strings are no longer needed.
Weak Object Pool in conjunction with the Flyweight pattern
The Flyweight pattern is a perfect match for the Weak Object Pool. The assumption behind this pattern is that the flyweight instances are shared to reduce memory consumption. The flyweight factory could use a Weak Object Pool to store the flyweights. This way, once a given flyweight is no longer in use, it will be released from the factory's storage and its memory reclaimed.
Words of caution
Don't go out using Weak Object Pools everywhere you have immutable classes instantiation. It is only an advantage to use it when there is a lot of repetition of the values. If the values are more randomly distributed, you will be better off not using this pattern, because it incurs in a small memory overhead for the internal map structures. If you apply the Weak Object Pool pattern in the wrong situation, you may end up with worse performance!
Also, it is very important to use this pattern only to store immutable classes . If you use this pattern for a non-immutable class, you can find yourself with bugs that are very difficult to reproduce and fix. Those bugs may happen if an instance of an object stored in the Weak Object Pool is shared by two completely unrelated clients, and one client modifies the instance. Then the other client will see the modified value and it will be very difficult to trace the original modification of the object.
More information:
- Wikipedia entry about the flyweight pattern
- WeakHashMap is not a cache!: My first article about the WeakHashMap class and the most common misconception about its applicability.
- Immutable Classes: an introductory article about immutable classes.


