The Heap Window and the Classes Window have to be two of the most overlooked pieces of functionality in JDeveloper's excellent debugger, and I've been using them heavily in the last couple of weeks to investigate memory leaks for the upcoming 9.0.5 release. I find the Heap Window even more useful than than the profiler for tracking down memory issues at runtime.... Here's an example of the classes and heap window in action...
One of the things we support in the version control support in JDeveloper is dynamic switching between version control systems (ClearCase, CVS, Oracle SCM) while JDeveloper is running. I wanted to be sure that when you switch from, say, Oracle SCM to CVS, the bulk of the implementation of Oracle SCM is made eligible for garbage collection so that it's not hogging resources unnecessarily.
So remote debugging JDeveloper with JDeveloper, I paused execution while Oracle SCM was the active version control system. Bringing up the Classes window (View->Debugger->Classes), there are a number of instances of implementation classes from the Oracle SCM client in memory, as expected:
I then resumed execution of the debugee, switched from Oracle SCM to CVS in the debugee, then paused execution again in the debugger. The JDeveloper toolbar has a useful garbage collection toolbar button which can be used to request that the process being debugged garbage collects. After using this, and inspecting the classes window again, I was surprised to see that an instance of the RSCStatusCache was still hanging around. Being a cache, this object could potentially be consuming a lot of memory, and there's no point it being on the heap while CVS is the active version control system.
So now I need to find out what's actually pinning this beastie on the heap and preventing the garbage collector from doing its job. That information is available on the Heap window. The easiest way to view it is to right click on a class in the Classes window you're interested in and choose the Display in Heap context menu.
The heap window shows two types of folder - class folders and reference path folders. Class folders contain a list of all instances of that class that are currently on the heap (The @1E5AF48C is an object reference for an instance of RSCStatusCache). Use the Show Reference Paths context menu on an object reference to add a reference path folder for that object. Expand that, and you can see all the chains of references to your object that exist.
In the above screenshot, I've selected the first reference path ("Static field Class oracle.ide.Ide"), and used the Expand Reference Path context menu to see the whole of that path. The _cache field in bold is the actual field that contains a reference to my leaking object.
The information in the heap window is enough to indicate that this leak is being caused by an anonymous inner class (IconOverlayTracker$3) that's listening to events on the singleton instance of oracle.ide.Ide. Anonymous inner classes have an implicit reference to their owning class (this$0), and it's this reference that's keeping VCSDefaultUtils.NodeOverlayTracker on the heap, and hence preventing my status cache from being garbage collected. Simply changing the code to remove the listener when the overlay tracker is deregistered solves the problem... No more leak :)
|