Memory Analyzer News

News from the Memory Analyzer

Immortal Objects - Or: How to Find Memory Leaks

This is a classic memory leak: Select Window -> New Window in your Eclipse IDE and then close the new window right away. The heap usage grows a little bit. Open and close a couple new windows and the heap usage grows more. That’s what bug 206584 is about. In this blog, I will use the “New Window” leak to explain how to find memory leaks using the Memory Analyzer.

A memory leak is an unintentional memory usage. In Java programs, leaks are objects which are not used/needed anymore, but which are still reachable and therefore are not removed by the Garbage Collector. In our case this means that instances of WorkbenchWindow are not garbage collected even though the window is closed and the workbench window instance is not needed anymore.

Once we got hold of the leaking object, we have to determine why it is still around. To do this, we follow the reference chain from the object to the Garbage Collection (GC) roots. GC roots are objects which are assumed to be reachable by the virtual machine: objects on the call stack of the current thread (e.g. method parameters and local variables), the thread itself and classes loaded by the system class loader. Looking at that reference path, we can decide which reference should be removed as it is accidentally keeping the leaking object alive.

Enough theory, let’s get started…

Reproduce the Leak

I did use an Eclipse 3.4M6a milestone build and reproduced the leak by using a default launch configuration for the product org.eclipse.platform.ide. Then I started the IDE, opened a new window via Window -> New Window and closed the window right away.

Acquire a Heap Dump

Now I need to create a heap dump. There are many ways to do this. On Windows, I find the easiest way is to use JRE 6 and JConsole. You can configure your JRE in the Eclipse Preferences: Java -> Installed JREs. Please note: you cannot use JConsole on JRE 5 VMs because the MBean does not provide the operation to write a heap dump.

Start <jre6>/bin/jconsole.exe and select the running Eclipse IDE:

JConsole Screenshot

Then, select the operation dumpHeap from the com.sun.management.HotSpotDiagnostic MBean. The first parameter p0 is the full path to the heap dump file. Make sure you give it the file extension .hprof. The second parameter p1 should be left at true as we are only interested in live objects.

JConsole Screenshot (Trigger Writing a Heap Dump)

Open the Heap Dump in the Memory Analyzer

Start the Memory Analyzer and select “Open Heap Dump…” from the File menu. The heap dump will be parsed and a number of index files is created next to the dump. Those are needed for fast access to the data in the dump.

Heap Dump Overview

The difficult part is to find the leaking object. In our case we can make it easy: we specifically test the “New Window” scenario so that’s what we are looking for. And we can assume some implementation knowledge: a workbench window is represented by the class WorkbenchWindow. So let’s open the histogram and filter (the first row) by that class:

Histogram

Et voilĂ , we have 2 instances even though we already closed the second window. Let’s have a look at those two objects. I choose the incoming references to see who is immediately referencing these WorkbenchWindow instances:

Histogram (Select Incoming References)

To decide which of the objects is good and which is bad, I open the incoming references. The second object is referenced by the Workbench via activeWorkbenchWindow. This looks okay, so the first one is suspicious.

Object List (Incoming References)

Path to Garbage Collection Roots

Via the context menu, I select to display the shortest path to the GC roots. I am excluding weak and soft references, because they cannot be the root cause for keeping the WorkbenchWindow alive.

Object List (Select Path to GC Roots)

Expanding the view a little bit, I get the following picture:

Path to GC Roots

What does it mean? These are the reference chains keeping the WorkbenchWindow alive. The name of the member variable that is holding the reference is printed in bold.

Analyzing the First Reference Path

To make sense of this, we need knowledge of the coding. I am not familiar with the code myself, but let’s see what we can dig up.

Path to GC Roots (First Reference Chain)

Looking at the first reference chain, I see the attribute listenerList of the Command object (A). I take a closer look because registering a listener and then forgetting to unregister the listener is a very easy way to create a leak. The Command objects are global and hence independent of the workbench window. A little further up, there is the anonymous inner class CommandAction$1 (B) in the listener list, which references the WorkbenchCommandAction (C) via this$0 (a reference automatically generated for non-static inner classes). Checking the source code, I know that WorkbenchCommandAction extends CommandAction. This is also a common pattern: the listener is an anonymous inner class.

Check if Listener is Removed

Okay, let’s get our hands dirty and take a look at the CommandAction code. This is the init method where we find the listener registration:

Code

The listener itself is created in the getCommandListener method. Note that a reference is kept as member variable. The reference is needed to unregister the listener.

create command listener

So let’s take a look at the dispose method:

dispose method

Yes, the listener is properly unregistered. No problem with this code.

Inspecting the Leak Object…

We need to know who forgets to call the dispose on the CommandAction. So let’s go back and have a look at the Command object. The Object Inspector (open the view via Window->Show View… if not yet open) shows the content of all member attributes. There we find the id attribute.

Path to GC Roots with Object Inspector

A quick code search with that id brings us to the ActionFactory which creates the WindowCommandAction object:

ActionFactory

Another search for references brings us to the WorkbenchActionBuilder. Looking at the code, the action is created and a reference is kept. But in the dispose method of the WorkbenchActionBuilder that reference is only nulled and not disposed. But after all, that’s just a one line code change to fix. (I attached a patch to the bug.)

Analyzing the Second Reference Path

It turns out that the second path has a very similar cause:

Path to GC Roots (Second Reference Chain)

Again, a WorkbenchCommandAction is involved. This time a class name in the reference chain gives us the hint: The BaseNewWizardMenu class uses the ActionFactory to create a command but does not dispose it properly. That is another one liner to fix.

If I now run the same scenario again, I only find one instance of the WorkbenchWindow in the histogram.

Conclusion

I hope you enjoyed this blog about analyzing a memory leak. Admittedly, we already knew what was leaking (the workbench window) and therefore could concentrate on the problem right away. One can use this approach for example when developing a new editor, view, plugin, perspective, etc. etc.

So what if you do not know what is leaking? Well, let me say this for now: let the leak grow until it shows up in the biggest objects. That is usually the case if your virtual machine throws an OutOfMemory (OoM) error. Make sure you configured your VM to write a heap dump on OoM (-XX:+HeapDumpOnOutOfMemoryError). Then run the Memory Analyzer.

Comments and Feedbacks are very welcome!

Hello (Memory Interested) World!

We are kicking off this blog with a traditional Hello World!. We will use this space to inform you about news from the Memory Analyzer project. The Memory Analyzer is a fast and feature-rich Java Heap Analyzer that helps you find memory leaks and reduce memory consumption.

Some links: Screenshots, Download, Wiki.

For those who have not been able to attend our EclipseCon session, you might want to join us at JAX or JavaOne. Here are the dates:

JAX, Wiesbaden, Germany
Automatisierte Speicheranalyse: Auf der Jagd nach den Speicherfressern
Speaker: Erwin Margewitsch
Wednesday, 23 April 2008, 10:00-11:00

JavaOne, San Francisco, CA, USA
Automated Heap Dump Analysis for Developers, Testers, and Support Employees
Speakers: Krum Tsvetkov, Andreas Buchen
Friday, 9 May 2008, 11:30-12:30

Please leave us your comments and feedbacks!

Recent Posts

Archives

Categories

Meta