I'll explain.
JHat (Java Heap Analysis Tool) is an experimental tool that comes with JDK 6. It can parse a heap dump file (HPROF format) and provide a simple http server (port 7000 as default) where we can execute queries in a simple SQL like language that has some predefined functions.
Although jhat is available as a command line tool, actually its classes are in the tools.jar file. So, I thought I could try do integrate that with my app. I tried to google some javadocs on jhat. Couldn’t find it. The tool is experimental, I forgot that.
I’ve tried to write the examples in a more didactic method in order to be more understandable. I hope I’ve put it in an easy way.
In order to run the example you would need an HPROF file.
Memory inspection done this way is not real time, since we are analyzing a snapshot of memory. But that helped me a lot. Remember that this is a heavy process that may consume lots of memory.
- Run jps to find out the PID of the Java application to take the memory snapshot
- Now run: jmap –dump:fomat=b,file=myheapdump.bin
- Now: java JHatExample myheapdump.bin
java –Xmx512m JHatQueryExample myheapdump.bin
- JHatExample: The GUI code
- JHatWrapper: A class that wraps the access to the jhat API
- MyQueryVisitor: Implementation of the jhat’s ObjectVisitor interface that provides a call back method on the results of the query.
With the same query from the picture, if you click execute it will show all referrers to all instances of java.io.File.
Actually the way I print the result of the query is quite confusing...
If anybody ever happen to try to run the code, there is a good link on constructing queries in Sundarajan's blog with nice examples. The code I provided makes some calls to the javascript library that is provided with JHat.
If you uncompress the tools.jar file you may find that library and the help page in:
tools.jar\com\sun\tools\hat\resources\
The purpose of this code is to show the possibility to integrate jhat usage into your application. Normally it is used as a stand alone tool. The stand alone tool is ready to use, but is a side tool that you have to open. What I did in my app (not the one shown here) was to automate the memory dump via a Runtime.exec call to jmap, and then the resulting file was used as a parameter to instatiate the jhat engine.
Since jhat is an experimental tool, it may completely change or become unavailable on Java 7. Use it like I did at your own risk.
Have fun playing with the code!
The GUI Code:
Now the wrapper class:
import java.awt.*;
import java.io.File;
import javax.swing.*;
public class JHatExample extends JFrame {
private JHatWrapper jhat;
public JHatExample(JHatWrapper jhat) {
super("JHat wrapper");
this.jhat = jhat;
initComponents();
}
private void initComponents() {
final JTextArea queryText = new JTextArea();
JPanel buttonPanel = new JPanel();
JButton executeBtn = new JButton("Execute");
buttonPanel.add(executeBtn);
this.add(new JScrollPane(queryText),BorderLayout.CENTER);
this.add(buttonPanel,BorderLayout.SOUTH);
executeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
jhat.query(queryText.getText());
} catch (Exception e1) {
JOptionPane.showMessageDialog(JHatExample.this, "Error executing query");
}
}
});
}
public static void main(String[] args) throws Exception {
if (args.length == 0 || ! new File(args[0]).exists()) {
System.out.println("The program must take a valid file path as parameter");
return;
}
JHatWrapper jhat = new JHatWrapper(args[0]);
JHatExample frame = new JHatExample(jhat);
frame.setSize(400,200);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
import java.io.*;
import com.sun.tools.hat.internal.model.Snapshot;
import com.sun.tools.hat.internal.oql.OQLEngine;
import com.sun.tools.hat.internal.parser.HprofReader;
import com.sun.tools.hat.internal.parser.PositionDataInputStream;
public class JHatWrapper {
private OQLEngine engine;
public JHatWrapper(String s) throws Exception {
PositionDataInputStream positiondatainputstream =
new PositionDataInputStream(new BufferedInputStream(new FileInputStream(s)));
int l;
Snapshot snapshot;
l = positiondatainputstream.readInt();
//I "inferred" the meaning of the parameters below by comparing the decompiled code
//and jhat's command line parameters
int debuglevel = 0;
//Don't know what this is...
int j =1;
HprofReader hprofreader = new HprofReader(s, positiondatainputstream,
j, true, debuglevel);
snapshot = hprofreader.read();
positiondatainputstream.close();
//resolve references
snapshot.resolve(true);
engine = new OQLEngine(snapshot);
}
public void query(String query) throws Exception {
engine.executeQuery(query, new MyQueryVisitor(engine) );
}
}
And the Visitor code:
import java.util.Enumeration;
import com.sun.tools.hat.internal.model.JavaHeapObject;
import com.sun.tools.hat.internal.oql.OQLEngine;
import com.sun.tools.hat.internal.oql.ObjectVisitor;
public class MyQueryVisitor implements ObjectVisitor {
private OQLEngine engine;
public MyQueryVisitor(OQLEngine engine) {
this.engine = engine;
}
public boolean visit(Object obj) {
try {
Object iterator = engine.call("wrapIterator",new Object[]{obj});
//Handles result items that are instance of enumeration. Ex: select referrers(x) from java.io.File x
if (iterator instanceof Enumeration) {
System.out.println("enum");
handleEnumeration((Enumeration)iterator);
} else {
//handles single result items. Ex: select x from java.io.File x
JavaHeapObject javaObj = (JavaHeapObject)engine.call("unwrapJavaObject",new Object[]{obj});
handleEnumeration(javaObj.getReferers());
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
void handleEnumeration(Enumeration en) {
while (en.hasMoreElements()) {
try {
Object obj = en.nextElement();
JavaHeapObject unwrapped = (JavaHeapObject)engine.call("unwrapJavaObject",new Object[]{obj});
System.out.println(unwrapped);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
1 comment:
well done,
that's what I need.
I will try it.
thanks
Post a Comment