When would you use a WeakHashMap or a WeakReference?
One problem with strong references is caching, particular with very large structures like images. Suppose you have an application which has to work with user-supplied images, like the web site design tool I work on. Naturally you want to cache these images, because loading them from disk is very expensive and you want to avoid the possibility of having two copies of the (potentially gigantic) image in memory at once.
Because an image cache is supposed to prevent us from reloading images when we don't absolutely need to, you will quickly realize that the cache should always contain a reference to any image which is already in memory. With ordinary strong references, though, that reference itself will force the image to remain in memory, which requires you to somehow determine when the image is no longer needed in memory and remove it from the cache, so that it becomes eligible for garbage collection. You are forced to duplicate the behavior of the garbage collector and manually determine whether or not an object should be in memory.
Understanding Weak References, Ethan Nicholas
WeakReference
versus SoftReference
One distinction to be clear on is the difference between a WeakReference
and a SoftReference
.
Basically a WeakReference
will be GC-d by the JVM eagerly, once the referenced object has no hard references to it. A SoftReference
d object on the other hand, will tend to be left about by the garbage collector until it really needs to reclaim the memory.
A cache where the values are held inside WeakReference
s would be pretty useless (in a WeakHashMap
, it is the keys which are weakly referenced). SoftReferences
are useful to wrap the values around when you want to implement a cache which can grow and shrink with the available memory.
One Common use of WeakReference
s and WeakHashMap
s in particular is for adding properties to objects. Occasionally you want to add some functionality or data to an object but subclassing and/or composition are not an option in that case the obvious thing to do would be to create a hashmap linking the object you want to extend to the property you want to add. then whenever you need the property you can just look it up in the map. However, if the objects you are adding properties to tend to get destroyed and created a lot, you can end up with a lot of old objects in your map taking up a lot of memory.
If you use a WeakHashMap
instead the objects will leave your map as soon as they are no longer used by the rest of your program, which is the desired behavior.
I had to do this to add some data to java.awt.Component
to get around a change in the JRE between 1.4.2 and 1.5, I could have fixed it by subclassing every component I was interested int (JButton
, JFrame
, JPanel
....) but this was much easier with much less code.
Another useful case for WeakHashMap
and WeakReference
is a listener registry implementation.
When you create something which wants to listen to certain events, usually you register a listener, e.g.
manager.registerListener(myListenerImpl);
If the manager
stores your listener with a WeakReference
, that means you don't need to remove the register e.g. with a manager.removeListener(myListenerImpl)
because it will be automatically removed once your listener or your component holding the listener becomes unavailable.
Of course you still can manually remove your listener, but if you don't or you forget it, it will not cause a memory leak, and it will not prevent your listener being garbage collected.
Where does WeakHashMap
come into the picture?
The listener registry which whishes to store registered listeners as WeakReference
s needs a collection to store these references. There is no WeakHashSet
implementation in the standard Java library only a WeakHashMap
but we can easily use the latter one to "implement" the functionality of the first one:
Set<ListenerType> listenerSet =
Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());
With this listenerSet
to register a new listener you just have to add it to the set, and even if it is not removed explicitly, if the listener is no longer referenced, it will be removed automatically by the JVM.