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!

Posted April 21st, 2008 by Andreas Buchen in category: Uncategorized
You can leave a response, or trackback from your own site.

13 Responses to “Immortal Objects - Or: How to Find Memory Leaks”


  1. Mariot Chauvin Says:

    Nice post !
    Hope to read you gain.


  2. Ed Merks Says:

    Great information! I’m filing this away for future reference!!


  3. Chris Aniszczyk Says:

    Very cool!


  4. Michael Scharf Says:

    Cool! I spend some time playing into the tool. It seems really cool.

    Is it available in source code or is it still in the IP review process?

    I like the idea of filters in the first row of the table. I have not seen it in any eclipse application before, but it is cool.

    What OQL implementation are you using? I like OQL. Many years ago I wrote a OQL engine in python (http://tinyurl.com/3tq3le). I think OQL/SQL would make a great query/filter language to be used in general for tables and trees (e.g. https://bugs.eclipse.org/bugs/show_bug.cgi?id=160113)… I have written a small OQL interpreter for WHERE expressions, but a more general implementation would be great…

    I look forward to see the code at eclipse.org….


  5. Manuel Selva Says:

    Great post !!!

    Your post could have saved me a lot of time trying to figure out how to generate an on demand basis memory dump. The answer is change my JDK from 1.5 to 6 !!!

    Would it be possible to have a button in the Memory Analyzer perspective allowing to directly browse a file and dump the memory into this file (See here http://manuelselva.wordpress.com/2008/01/ and here http://manuelselva.wordpress.com/2008/01/16/profile-your-eclipse-plugins-with-sap-memory-analyzer/ post + comments) ??

    Manu


  6. Andreas Buchen Says:

    Michael:

    > What OQL implementation are you using?

    That’s actually a home grown OQL. We use javacc to compile the grammar. Check out the help for some details. It is very SQL-like…

    > I look forward to see the code at eclipse.org….

    You do not have to look further. The code is already available. We use the Subversion Repository though.

    Andreas.


  7. Andreas Buchen Says:

    Manu:

    thanks for blogging a/b the SAP Memory Analyzer! (for all who do not know the history: the Memory Analyzer grew up at SAP. About 2 years ago we experienced memory problems but at the same time server heap sizes grew to some 40 to 50 million objects. There was nothing really available to look into those heap dumps in a reasonable time. So we started writing a HPROF parser… and now we contributed it to the Eclipse foundation)

    About acquiring heap dumps from inside Eclipse: We have this on our list for some time now. We’d need some dialog and an extension point for “heap writers”. One implementation would use the HotspotDiagnostics MBean, another could use jps and jmap (available on Linux, Mac OS X, …). Are you interested in a contributing?

    Andreas.


  8. Michael Scharf Says:

    > You do not have to look further. The code is already available.
    > We use the Subversion Repository though.

    Cool. But where do I find it? (It is often very hard to find information like the repository path from eclipse project sites)

    Michael


  9. Michael Scharf Says:

    i assume it is: svn://dev.eclipse.org/svnroot/technology/org.eclipse.mat/trunk/


  10. Andreas Buchen Says:

    Source Code Web Access
    http://dev.eclipse.org/viewsvn/index.cgi/org.eclipse.mat/trunk/?root=Technology_SVN

    Subversion URL
    https://dev.eclipse.org/svnroot/technology/org.eclipse.mat/trunk/


  11. Jacek Pospychala Says:

    awesome! it’s a good start for article to http://www.eclipse.org/articles


  12. Manuel Selva Says:

    Andreas,

    > Are you interested in a contributing?

    yes of course.

    Manu


  13. Memory Analyzer News » Blog Archive » The Unknown Generation: Perm Says:

    [...] deployed this servlet inside a WAR and then re-deployed it again. Then I took a heap dump (see my previous blog or Memory Analyzer’s [...]

Leave a Reply

You must be logged in using your Eclipse Bugzilla account to post a comment.

Recent Posts

Archives

Categories

Meta