Heap analysis: locating memory leaks
A primary objective of application developers looking to profile memory use is either to:
- Analyze the contents of the heap as the application is running (allowing class-by-class statistics to be generated)
- Identify memory leaks by utilizing either heap analysis on-the-fly or heap analysis after a user-requested garbage collection (to identify objects that are or are not being not garbage collected).
To begin to gather heap information from your application, launch your application from the Profiling dialog, using the Memory Analysis profiling type. The only profiler option available in memory analysis is whether or not to Track Object Allocation Sites. Allocation sites are the locations in the code where objects are being instantiated (implicitly or explicitly). With this option selected, you are able to select classes in the Object Allocations view, and discern from which methods those objects were created.
The only disadvantage to having the Track Object Allocation Sites option selected is that it will significantly increase the volume of profiling data being generated. If profiling performance is impacted, or workbench responsiveness is affected, consider either clearing this option or reducing the amount of data by using an updated filter set.
In addition to object allocation, ensure that you have set the correct filters for your application. Heap profiling has a greater overall instrumentation overhead associated with it than execution or thread analysis, which itself already imposes a potentially hefty penalty to application performance. Finally, when selecting filters: if you want to see, for instance, space occupied by Java types such as
Integer, you will need to add these to the filter. By default, these are filtered out by the
When data becomes available from the profiled application, it is displayed in the Object Allocation view. The Object Allocation view is the main view in which all of the information gathered by the heap profiling agent is presented.
Object Allocation columns:
- Live Instances: The current number of objects in the heap, of the specified class, that are presently being utilized (have not been garbage collected).
- Total Instances: The total number of objects in the heap that have been created during the JVM's lifetime (including those that have been garbage collected).
- Active Size (bytes): The total size of all of the object instances, of the particular class, that are presently used by the JVM (in other words, that have not been garbage collected). Note that object size is JVM-implementation dependent.
- Total Size (bytes): The total size of all object instances of the class, including those that have been garbage collected earlier in the application lifecycle.
- Average Age: The average age of an object before it is garbage collected, as measured by the number of garbage collections that this object has survived. Objects that have survived a large number of garbage collections are considered to be a memory leak if their usage is no longer required by the application.
The data in the memory statistics table can be switched between package level and class level using the view toolbar. This can be especially helpful when you are dealing with a large number of classes. The data can be represented as a percentage or as a delta of existing data from the previous refresh. Figure 7 shows the icons for, from left to right, report generation, filtering, package/class view options, and the percentage and delta options.
Figure 7. Icons in the Object Allocations view toolbar
If you have the Object Allocation profiling option selected, then double-clicking any of the entries in the Memory Statistics table will switch to the Allocation Details tab. This tab presents a table of all of the locations in the program where objects of that type were allocated. When a particular type has been identified as being over-represented in the heap, it can be particularly helpful for you to use the data in the view to determine where those objects are being allocated, allowing the identification and elimination of excessive allocation.
When the workbench has begun collecting profiling data from the target application, you can consult the Object Allocation view at any time in order to determine the present content of the heap. The table will reflect, in real time, all object allocation and de-allocation events as they occur. This provides a moment-to-moment view of the memory contents, expressed either as a percentage of the total, or in absolute terms (bytes).
By sorting the data table and selecting the classes with the largest active size, developers can target problem areas in need of improvement. The allocation details for those classes then provide a list of the object creation sources, which you can then, one-by-one, rule out or investigate as being a cause of the heap size issues.
This example represents a simple chat room Web application that allows users to log in and communicate with each other. Room chatter is transmitted to all participants, and then all conversations are written to a log file on the server. However, using the Heap profiling capabilities of Rational Application Developer, you have identified a serious memory leak (likely involving one specific class).
In the Memory Statistics view with the total percentage listed, you can see that the
ChatlineMessage class represents nearly 98% of the objects presently allocated in the heap, composing 61% of the total heap contents by size. This should be a serious warning sign to the application developer that one or more classes are over-represented in the heap contents, and are contributing to a memory leak in the application.
Figure 8. Viewing by package with the delta option selected
The Profiling Monitor view also allows you to request garbage collection of the JVM. When used in combination with the delta table, this is especially helpful in determining how much of the heap is contained in irretrievable heap objects, which are another type of memory leak.
Before selecting garbage collection, select the Show Delta Columns icon from the Object Allocations toolbar. This introduces four new delta columns, which are delta versions of the existing columns and reflect changes in these values from when you previously selected the Refresh button (see Figure 9, following). When you are in the delta table, click the Run Garbage Collection (green play arrow with a garbage can) icon from the Profiling Monitor toolbar to trigger a JVM garbage collection. The results of the garbage collection will be reflected in the Memory Statistics table as soon as you select the refresh button, as shown in Figure 9 following.
Those classes that contributed the most to the garbage collected heap contents can be identified by sorting by the Delta: Active Size table column. These are the classes that lost the most size during the collection, and which contributed the most to the total unused object pool.
When the JVM performs a garbage collection, it looks for allocated objects in the heap that are orphaned (that is, they are not referenced by any other object in the heap, which itself is not referenced by any heap object.) Using the same chat application example as discussed previously, you have now instructed the JVM to perform a garbage collection through the workbench. You can see that the initial result is a major decrease in both the number of allocated
ChatlineMessage objects, as well as the total size of the heap.
Figure 9. The delta columns provide moment-to-moment statistics on object collection
This screen capture shows the contents of the Memory Statistics view a moment after you have requested a garbage collection. Already, the contents of the heap have been reduced dramatically, with 22,079 fewer live instances and a corresponding percentage drop. Within about five or six seconds, the Live Instances percentage will drop to nearly zero. In this application, you discover that
ChatlineMessage objects are being allocated, used briefly, and then discarded.
Using heap analysis, delta columns, and garbage collection, you can identify objects that are being allocated but not garbage collected, or that are not referenced by any other objects in the application lifecycle and are contributing to a large collection of orphaned objects. Application developers can analyze and reduce the overall footprint of their application, and can potentially reduce system swapping penalties, improve response time, and reduce application system requirements and cost.