September 22, 2007

Java Memory Leaks

Yesterday is stumbled upon some interesting blog posts by Scott Violet from the (former) Swing Team and Tim Boudreau from Netbeans. Apparently Swing action listeners before Java 6.0 were registered, and as such tied with normal references instead of Weak References causing unused objects to remain in memory. So if you're developing in pre-6.0 be sure to think about this issue, you're most likely preventing unused memory for liberation by the garbage collector (= memory leak, yes Java has memory leaks too!). Second concern is that still a lot of Swing developers tend to extends Swing components:
public class MyFrame extends JFrame {}
This has the unfortunate effect that the GC needs more passes (at least 2) to recollect unused memory. This is because somewhere up in the inheritance graph Component implements finalize() causing the GC to do extra effort to collect memory because it needs to run the finalizer and then reschedule another GC pass. So in case you're just USING the Swing components and not adding features, use them like you use other Java objects like ArrayList, HashMap,... It's a quote from Erich Gamma: 'Favor composition over inheritance'.

Although memory leaks are more likely to happen in combination with, they're not limited to Swing. There are other concerns: static inner classes, misplaced fields with respect to their scope, collections used as cache, ... You don't do it on purpose and there are commercial profilers that do a good job in detecting these issues but you need to find them yourself. However the Netbeans team has a nice tool to test for these errors. The extended the JUnit TestCase with a method: assertGC() to test if a certain object reference can be GCed or not. If not they launch the INSANE library to detect which chain of objects is still referencing your object, and so preventing it from garbage collection. Their files can be found here with an easy example: Tim Boudreau in his LeakDemo source code. Take note that the example now correctly passes the JUnit test because of the adaptions in Swing from Java 6.0, but it explains how to work with the test-cases.