The jhat
tool provides a convenient means to browse the object topology in a heap snapshot. This tool replaces the Heap Analysis Tool (HAT).
For more details on the jhat
utility, see the jhat
command man page.
The tool parses a heap dump in binary format (for example, a heap dump produced by jmap -dump
).
This utility can help debug unintentional object retention. This term is used to describe an object that is no longer needed but is kept alive due to references through some path from the rootset. This can happen, for example, if an unintentional static reference to an object remains after the object is no longer needed, if an Observer or Listener fails to unregister itself from its subject when it is no longer needed, or if a Thread that refers to an object does not terminate when it should. Unintentional object retention is the Java language equivalent of a memory leak.
The following sections describe troubleshooting techniques for jhat utility.
The tool provides a number of standard queries. For example, the Roots query displays all reference paths from the rootset to a specified object and is particularly useful for finding unnecessary object retention.
In addition to the standard queries, you can develop your own custom queries with the Object Query Language (OQL) interface.
When you issue the jhat
command, the utility starts an HTTP server on a specified TCP port. You can then use any browser to connect to the server and execute queries on the specified heap dump.
Example 2-17 shows how to execute jhat
to analyze a heap dump file named snapshot.hprof
:
Example 2-17 Analyse Heap Dump with jhat
$ jhat snapshot.hprof
Started HTTP server on port 7000
Reading from java_pid2278.hprof...
Dump file created Fri May 19 17:18:38 BST 2006
Snapshot read, resolving...
Resolving 6162194 objects...
Chasing references, expect 12324 dots................................
Eliminating duplicate references.....................................
Snapshot resolved.
Server is ready.
At this point, jhat
has started an HTTP server on port 7000. Point your browser to http://localhost:7000
to connect to the jhat
server.
When you are connected to the server, you can execute a standard query, see Standard Queries or create an OQL query, see Custom Queries. The All Classes query is displayed by default.
When you have connected to the jhat
server, you can execute the following standard queries:
All Classes Query
The default page is the All Classes query, which displays all of the classes present in the heap, excluding platform classes. This list is sorted by fully qualified class name, and broken out by package. Click the name of a class to go to the Class query.
The second variant of this query includes the platform classes. Platform classes include classes whose fully qualified names start with prefixes such as java
, sun
., javax.swing.
, or char[
. The list of prefixes is in a system resource file called /resources/platform_names.txt
. You can override this list by replacing it in the JAR file, or by arranging for your replacement to occur first on the classpath when jhat
is invoked.
Class Query
The Class query displays information about a class. This includes its superclass, any subclasses, instance data members, and static data members. From this page you can navigate to any of the classes that are referenced, or you can navigate to an Instances query.
Object Query
The Object query provides information about an object that was on the heap. From here, you can navigate to the class of the object and to the value of any object members. You can also navigate to objects that refer to the current object. Perhaps the most valuable query is at the end: the Roots query reference chains from the rootset.
Note: The object query also provides a stack backtrace of the point of allocation of the object.
Instances Query
The Instances query displays all instances of a given class. The allInstances
variant includes instances of subclasses of the given class as well. From here, you can navigate back to the source class, or you can navigate to an Object query on one of the instances.
Roots Query
The Roots query displays reference chains from the rootset to a given object. It provides one chain for each member of the rootset from which the given object is reachable. When calculating these chains, the tool does a depth-first search, so that it will provide reference chains of minimal length.
There are two kinds of the Roots query: one that excludes weak references (Roots), and one that includes them (All Roots). A weak reference is a reference object that does not prevent its referent from being made finalizable, finalized, and then reclaimed. If an object is only referred to by a weak reference, then it is usually not considered to be retained, because the garbage collector can collect it as soon as it needs the space.
Note: This is probably the most valuable query injhat for debugging unintentional object retention. When you find an object that is being retained, this query tells you why it is being retained. |
Reachable Objects Query
This query is accessible from the Object query and shows the transitive closure of all objects reachable from a given object. This list is sorted in decreasing size, and alphabetically within each size. At the end, the total size of all of the reachable objects is given. This can be useful for determining the total runtime footprint of an object in memory, at least in systems with simple object topologies.
This query is most valuable when used in conjunction with the -exclude
command-line option. This is useful, for example, if the object being analyzed is an Observable. By default, all of its Observers would be reachable, which would count against the total size. The -exclude
option allows you to exclude the data members java.util.Observable.obs
and java.util.Observable.arr
.
Instance Counts for All Classes Query
This query shows the count of instances for every class in the system, excluding platform classes. It is sorted in descending order, by instance count. A good way to spot a problem with unintentional object retention is to run a program for a long time with a variety of input, and then request a heap dump. Looking at the instance counts for all classes, you may recognize a number of classes because there are more instances than you expect. Then you can analyze them to determine why they are being retained (possibly using the Roots query). A variant of this query includes platform classes.
For more information about platform classes, see All Classes Query.
All Roots Query
This query shows all members of the rootset, including weak references.
For more information about weak references, see Roots Query.
New Instances Query
The New Instances query is available only if you invoke the jhat
server with two heap dumps. This query is similar to the Instances query, except that it shows only new instances. An instance is considered new if it is in the second heap dump and there is no object of the same type with the same ID in the baseline heap dump. An object's ID is a 32-bit or 64-bit integer that uniquely identifies the object.
Histogram Queries
The built-in histogram and finalizer histogram queries also provide useful information.
You can develop your own custom queries with the built-in Object Query Language (OQL) interface. Click the Execute OQL Query button on the first page to display the OQL query page, where you can create and execute your custom queries. The OQL Help facility describes the built-in functions, as shown in Example 2-18.
The syntax of the select
statement is as follows:
To get useful information from jhat
often requires some knowledge of the application and the libraries and APIs that it uses. You can use jhat
to answer two important questions:
What is keeping an object alive?
When you view an object instance, you can check the objects listed in the section entitled "References to this object" to see which objects directly reference this object. More importantly, you can use the Roots query to determine the reference chains from the root set to the given object. These reference chains show a path from a root object to this object. With these chains, you can quickly see how an object is reachable from the root set.
As noted earlier, the Roots query excludes weak references, whereas All Roots query includes them. A weak reference is a reference object that does not prevent its referent from being made finalizable, finalized, and then reclaimed. If an object is only referred to by a weak reference, then the garbage collector can collect it as soon as it needs the space.
The jhat
tool sorts the rootset reference chains by the type of the root, in the following order:
Static data members of Java classes.
Java local variables. For these roots, the thread responsible for them is shown. Because a thread is a Java object, this link is clickable. This allows you, for example, to easily navigate to the name of the thread.
Native static values.
Native local variables. Again, such roots are identified by their thread.
Where was this object allocated?
When an object instance is being displayed, the section entitled "Objects allocated from" shows the allocation site in the form of a stack trace. In this way, you can see where the object was created.
Note: This allocation site information is available only if the heap dump was created with HPROF using the heap=all
option. This HPROF option includes both the heap=dump
option and the heap=sites
option. For more information about HPROF and its options, see HPROF.
If the leak cannot be identified using a single object dump, then another approach is to collect a series of dumps and to focus on the objects created in the interval between each dump. The jhat
tool provides this capability using the -baseline
option.
The -baseline
option allows two dumps to be compared if they were produced by HPROF and from the same VM instance. If the same object appears in both dumps, then it will be excluded from the list of new objects reported. One dump is specified as a baseline, and the analysis can focus on the objects that are created in the second dump since the baseline was obtained.
Example 2-19 shows how to specify the baseline.
In the preceding example, the two dumps are in the file snapshot.hprof
, and they are distinguished by appending #1
and #2
to the file name.
When jhat
is started with two heap dumps, the Instance Counts for All Classes query includes an additional column that is the count of the number of new objects for that type. An instance is considered new if it is in the second heap dump and there is no object of the same type with the same ID in the baseline. If you click a new count, then jhat
lists the new objects of that type. Then for each instance, you can view where it was allocated, which objects these new objects reference, and which other objects reference the new object.
In general, the -baseline
option can be very useful if the objects that need to be identified are created in the interval between successive dumps.