For those who enjoy design patterns, I’ve written a ProxyIcon for the MMORPG client. The reason is to decrease memory allocation in the client, which is due in large part to graphic images, under the theory that most are not needed most of the time. This article describes the ProxyIcon (implementation details are left as an exercise for the reader). First, there is already a central factory of Icons. So, integrating a new proxy pattern only involved changing the factory. ProxyIcon implements the javax.swing.Icon interface, so users are none the wiser.
ProxyIcon knows the source of the image to load. When any Icon methods are accessed, it makes sure the internal image is loaded. If not accessed for some timeout, the internal icon can be unloaded. The simplest implementation would have set the internal Icon reference to null, allowing the garbage collector to clean it up. Why the present perfect tense? Because there’s an even better approach:
ProxyIcon holds two references to the internal Icon: A hard reference and a soft reference. After the timeout, the hard reference is set null, but the soft reference is retained. Since the embedded icon is only referenced (never exposed) from the ProxyIcon object, this leaves only the soft reference. Java cleans up soft references (only) when memory is needed. If, however, the ProxyIcon is accessed before being cleaned up, the hard reference is reinstated. This provides the best of both worlds: Application logic can set policy on when to release resources but is backed up by the Java Virtual Machine’s garbage collection logic to avoid unhelpful cleanup.
The primary benefit, of course, is that unused icon resources can be deallocated. The software design benefit is that image users do not need to concern themselves with whether the icon is currently loaded or not. Notice that all ProxyIcon references stay in memory forever (in the factory). They could also be deallocated by using soft or weak references in the factory, but the objects themselves are (theoretically) miniscule compared to image memory consumption.
Another minor benefit is lazy image loading. Instead of loading upon construction, the internal icon is loaded lazily upon first access. There are a number of icons that are requested in the code, but not always used. This feature has the benefit that the code can greedily ask for icons from the factory (e.g., as static variables) without impacting memory if not needed.
One major gotcha is that Java itself may be holding cached references to the image data, which prevents garbage collection. I have not investigated this fully, but do not use Toolkit.getImage(), as it’s API warns of just this problem. Also read the code for javax.swing.ImageIcon, which makes (undocumented) use of Toolkit.getImage(). Instead, use Toolkit.createImage() or ImageIO.read() with ImageIO.setUseCache(false). Further investigation is needed to determine exactly who has access to underlying image data depending on how it is loaded.
For Alpha testers, look at $HOME/.potentialrpg/stat/IconFactory.stat, which shows:
timestamp, total, loaded, hard_referenced, average_ms_timeout, ms_stat_time