Tuesday, October 16, 2007

Programatically Dump Java Heap

Lately I have been tracking down memory leaks in OpenEJB using Your Kit. For some reason Your Kit throws verifier exceptions on my Mac when attempting to monitor my application. At first I tried switched to JProbe, but it simply reports incorrect heap view. Then Kevan suggested that I use -XX:+HeapDumpOnOutOfMemoryError to get the VM to dump heap when the OutOfMemoryError occurs, and use Your Kit to perform a post-mortem which worked to find the major leaks.

Now, I'm trying to release JCA connections when they are GCed, and in this case I'm not getting an OutOfMemoryError but a ResourceException which means I don't get a heap dump. Turning to Google again, I found this blog by A. Sundararajan on how to programatically dump the Java heap. I rewrote the code to use reflection to avoid a dependency on the Sun class and changed it to never throw an exception. Simply pop this in your code, and then open the HPROF heap with Your Kit.


/**
* Dumps the java heap to the specified file in hprof format.
* This method will not overwrite the dump file, so make sure it doesn't already exist.
* @param fileName the dump file name which must not already exist.
*/
public static void dumpHeap(String fileName) {
Class clazz;
try {
clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
} catch (ClassNotFoundException e) {
System.out.println("ERROR: dumpHeap only works on a Sun Java 1.6+ VM containing " +
"the class com.sun.management.HotSpotDiagnosticMXBean");
return;
}

// use JMX to find hot spot mbean
Object hotspotMBean = null;
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(server,
"com.sun.management:type=HotSpotDiagnostic",
clazz);
} catch (Throwable e) {
System.out.print("ERROR: dumpHeap was unable to obtain the HotSpotDiagnosticMXBean: ");
e.printStackTrace();
}

// invoke the dumpHeap method
try {
Method method = hotspotMBean.getClass().getMethod("dumpHeap", String.class);
method.invoke(hotspotMBean, fileName);
} catch (InvocationTargetException e) {
Throwable t = e.getCause() != null ? e.getCause() : e;
System.out.print("ERROR: dumpHeap threw an exception: ");
t.printStackTrace();
} catch (Throwable e) {
System.out.print("ERROR: dumpHeap threw an exception: ");
e.printStackTrace();
}
}


Alternatively, if you just want heap information without adding code, you can use jconsole to execute the dumpHeap method, or "jmap -dump:file=FILENAME PID" to get dump. Of course this heap dump won't be from the exact point where your connection pool ran out of connections.

Monday, October 15, 2007

Disable DTD and XSD downloading

This is another thing that has been bugging me for a while. In OpenEJB we use JaxB to parse all the JEE deployment descriptors (e.g, ejb-jar.xml, web.xml, application.xml, etc.). Since, we not validating in JaxB, I don't want the schemas file downloaded, but for some reason the xml parser attempts downloads DTD files even though we have validation disabled.

Anyway, I googled for information about schema downloading and found tons of articles on how to use Apache XML Resolver to redirect remote schemas to local files. This is nice and all, but I don't want the schemas processed at all. Of course there are no articles covering the simple "disable all this crap" case.

After a bit of hacking I found that you need an EntityResolver which returns an empty InputSource from resolveEntity. This must be an empty InputSource and not null as null causes the default (downloading) resolver to kick-in. Here is the code:


public static class NamespaceFilter extends XMLFilterImpl {
private static final InputSource EMPTY_INPUT_SOURCE =
new InputSource(new ByteArrayInputStream(new byte[0]));

public NamespaceFilter(XMLReader xmlReader) {
super(xmlReader);
}

public InputSource resolveEntity(String publicId, String systemId) {
return EMPTY_INPUT_SOURCE;
}
}


And, if you want to integrate this into JaxB, do something like this:


public static Object unmarshal(Class type,
InputStream in,
boolean logErrors) throws Exception {

// create a parser with validation disabled
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
SAXParser parser = factory.newSAXParser();

// Get the JAXB context -- this should be cached
JAXBContext ctx = JAXBContext.newInstance(type);

// get the unmarshaller
Unmarshaller unmarshaller = ctx.createUnmarshaller();

// log errors?
unmarshaller.setEventHandler(new ValidationEventHandler(){
public boolean handleEvent(ValidationEvent validationEvent) {
if (logErrors) {
System.out.println(validationEvent);
}
return false;
}
});

// add our XMLFilter which disables dtd downloading
NamespaceFilter xmlFilter = new NamespaceFilter(parser.getXMLReader());
xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler());

// Wrap the input stream with our filter
SAXSource source = new SAXSource(xmlFilter, new InputSource(in));

// unmarshal the document
return unmarshaller.unmarshal(source);
}

Labels:

Tuesday, October 2, 2007

Java getPid()

Ever since I started programming in Java it has bugged me that I can't get the process id of the Java VM. Well today I was browsing the OpenJPA code and found this gem:

private static String getPid() {
// This relies on the undocumented convention of the
// RuntimeMXBean's name starting with the PID, but
// there appears to be no other way to obtain the
// current process' id, which we need for the attach
// process
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
String pid = bean.getName();
if (pid.indexOf("@") != -1) {
pid = pid.substring(0, pid.indexOf("@"));
}
return pid;
}

Wednesday, June 13, 2007

Short life expectancy

About 6 months ago, I move to DreamHost, and ever since I have been planning on clicking the install WordPress button (yes, it is a One-Click Install at DreamHost). This is actually my second attempt at blogging, and with this single post my most successful yet! Years ago, my friend, Alex, spent his valuable time setting up Movable Type for me, and I never used it, even once. As with most thing non-programming related, I'll most likely add a few entries over the next few months, and then never post again, such is my one track mind.

So with the life expectancy of my blog being 3-6 months, what can you expect for your time? Well, you should expect to see the dumb ideas running through my head related to what ever I happen to be doing, which lately has been programming, photography, surfing and watching tv. I really don't like writing that much, so you should expect short infrequent posts. I also don't read or participate in other people's blogs, so expect wackiness with my blog setup, and as always expect me to make just about every mistake (fun for everyone).

Now, lets see if I can get Gallery installed.