I’ve been using a lot of weak references lately. A couple of years ago I’ve read a blog entry that said that a lot of experienced Java developers did not know what they were. I’m glad I already knew it at the time I’ve read that :)
I won’t go into details of weak references. I’ll just say that an object is not prevented to be garbage collected if it only has weak references pointing to it. You can get a nice overview here.
In my current project I’m tracking garbage collection of special types of objects by using weak references. Maybe in another post I’ll provide more info on that.
Throughout my career I’ve used that nice resource of Java a few times. The most important ones that I can remember are:
- Weak Listeners
- Memory Sensitive Caches
Weak Listeners
Memory leaks are a painful problem that sometimes we don’t have a clue how to find them. I’ve worked in a desktop project where we had lots of instantiations of GUI panels, models, etc. That implied in a bunch of model listeners, property listeners, and so forth. We had a major memory leak due to a simple problem in most of the panels: objects from each created panel were being added as a listener of the model, but after the panel was discarded they did not remove themselves from the listeners list of the model. So, they kept being reference by the listeners list on the model and could not be garbage collected.
A naïve example tries to illustrate that:
public void setActivePanel() {
MyPanel thePanel = new MyPanel();
ApplicationModel.getInstance().addXYZListener(thePanel);
this.activePanel = thePanel;
}
public void releaseActivePanel() {
this.activePanel = null;
this.clearSelection();
}
One solution was to provide a way to unsubscribe "thePanel" from the XYZListener list:
public void releaseActivePanel() {
ApplicationModel.getInstance().addXYZListener(this.activePanel);
this.activePanel = null;
this.clearSelection();
}
But.... you could also use weak references. They would avoid such problems. At the time I found good stuff that I’ve used as a reference.
The only issue that I see in his solution is that we may have sometimes a bunch of empty WeakPropertyChangeListeners (pointing to null) when the referred object is GC’d. He provides a lazy approach for clean up. However, if you use your own ReferenceQueue you can provide a thread that once in a while polls the queue and does the sweeping of listener lists. Or also you can use a WeakHashMap which does the dirty work of clean up.
Memory sensitive caches
There are several caching strategies (e.g. LRU, MRU) but this one is very simple. It caches everything, but when the system runs out of memory the cache is cleaned up.
Depending on the memory footprint of your system this strategy may be useless and your cache will not work really well.
This memory sensitive cache relies on SoftReferences. They are special types of WeakReference which hang around for a while.
The following class is not an optimal implementation of a cache, but it works…
public class ImageCache {
private HashMap<String, SoftReference<ImageIcon>> map = new HashMap<String, SoftReference<ImageIcon>>();
private static final String IMAGE_FOLDER = "/images/";
public ImageIcon getImage(String imageFile) {
ImageIcon icon = null;
if (map.containsKey(imageFile)) {
icon = map.get(imageFile).get();
if (icon == null) {
icon = cacheImage(imageFile);
}
} else {
icon = cacheImage(imageFile);
}
return icon;
}
private ImageIcon cacheImage(String imageFile) {
ImageIcon icon = new ImageIcon(this.getClass().getResource(
imageFile));
map.put(imageFile, new SoftReference<ImageIcon>(icon));
return icon;
}
}
Another cool stuff that can be done (although I've never done it in a "real life" project as I did with the other stuff) is to provide an alternative way to finalization using PhantomReferences. You can use them combined with a ReferenceQueue to provide your own finalization mechanism avoiding the error-prone finalize method. You could write your own finalizer thread :)
There is a good reference here.
I guess that it for the moment.