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