Add support for dumping the heap on OpenJ9

Closes gh-26466
pull/27538/head
Andy Wilkinson 3 years ago
parent e30bf0e3e5
commit 64c0eceec7

@ -7,7 +7,10 @@ The `heapdump` endpoint provides a heap dump from the application's JVM.
[[heapdump.retrieving]] [[heapdump.retrieving]]
== Retrieving the Heap Dump == Retrieving the Heap Dump
To retrieve the heap dump, make a `GET` request to `/actuator/heapdump`. To retrieve the heap dump, make a `GET` request to `/actuator/heapdump`.
The response is binary data in https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html[HPROF] format and can be large. The response is binary data and can be large.
Its format depends upon the JVM on which the application is running.
When running on a HotSpot JVM the format is https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html[HPROF]
and on OpenJ9 it is https://www.eclipse.org/openj9/docs/dump_heapdump/#portable-heap-dump-phd-format[PHD].
Typically, you should save the response to disk for subsequent analysis. Typically, you should save the response to disk for subsequent analysis.
When using curl, this can be achieved by using the `-O` option, as shown in the following example: When using curl, this can be achieved by using the `-O` option, as shown in the following example:

@ -100,26 +100,38 @@ public class HeapDumpWebEndpoint {
if (this.heapDumper == null) { if (this.heapDumper == null) {
this.heapDumper = createHeapDumper(); this.heapDumper = createHeapDumper();
} }
File file = createTempFile(live); File file = createTempFile();
this.heapDumper.dumpHeap(file, live); this.heapDumper.dumpHeap(file, live);
return new TemporaryFileSystemResource(file); return new TemporaryFileSystemResource(file);
} }
private File createTempFile(boolean live) throws IOException { private File createTempFile() throws IOException {
String date = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm").format(LocalDateTime.now()); String date = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm").format(LocalDateTime.now());
File file = File.createTempFile("heapdump" + date + (live ? "-live" : ""), ".hprof"); File file = File.createTempFile("heap-" + date, "." + determineDumpSuffix());
file.delete(); file.delete();
return file; return file;
} }
private String determineDumpSuffix() {
if (this.heapDumper instanceof OpenJ9DiagnosticsMXBeanHeapDumper) {
return "phd";
}
return "hprof";
}
/** /**
* Factory method used to create the {@link HeapDumper}. * Factory method used to create the {@link HeapDumper}.
* @return the heap dumper to use * @return the heap dumper to use
* @throws HeapDumperUnavailableException if the heap dumper cannot be created * @throws HeapDumperUnavailableException if the heap dumper cannot be created
*/ */
protected HeapDumper createHeapDumper() throws HeapDumperUnavailableException { protected HeapDumper createHeapDumper() throws HeapDumperUnavailableException {
try {
return new HotSpotDiagnosticMXBeanHeapDumper(); return new HotSpotDiagnosticMXBeanHeapDumper();
} }
catch (HeapDumperUnavailableException ex) {
return new OpenJ9DiagnosticsMXBeanHeapDumper();
}
}
/** /**
* Strategy interface used to dump the heap to a file. * Strategy interface used to dump the heap to a file.
@ -140,8 +152,8 @@ public class HeapDumpWebEndpoint {
} }
/** /**
* {@link HeapDumper} that uses {@code com.sun.management.HotSpotDiagnosticMXBean} * {@link HeapDumper} that uses {@code com.sun.management.HotSpotDiagnosticMXBean},
* available on Oracle and OpenJDK to dump the heap to a file. * available on Oracle and OpenJDK, to dump the heap to a file.
*/ */
protected static class HotSpotDiagnosticMXBeanHeapDumper implements HeapDumper { protected static class HotSpotDiagnosticMXBeanHeapDumper implements HeapDumper {
@ -171,6 +183,38 @@ public class HeapDumpWebEndpoint {
} }
/**
* {@link HeapDumper} that uses
* {@code openj9.lang.management.OpenJ9DiagnosticsMXBean}, available on OpenJ9, to
* dump the heap to a file.
*/
private static final class OpenJ9DiagnosticsMXBeanHeapDumper implements HeapDumper {
private Object diagnosticMXBean;
private Method dumpHeapMethod;
@SuppressWarnings("unchecked")
private OpenJ9DiagnosticsMXBeanHeapDumper() {
try {
Class<?> mxBeanClass = ClassUtils.resolveClassName("openj9.lang.management.OpenJ9DiagnosticsMXBean",
null);
this.diagnosticMXBean = ManagementFactory.getPlatformMXBean((Class<PlatformManagedObject>) mxBeanClass);
this.dumpHeapMethod = ReflectionUtils.findMethod(mxBeanClass, "triggerDumpToFile", String.class,
String.class);
}
catch (Throwable ex) {
throw new HeapDumperUnavailableException("Unable to locate OpenJ9DiagnosticsMXBean", ex);
}
}
@Override
public void dumpHeap(File file, boolean live) throws IOException, InterruptedException {
ReflectionUtils.invokeMethod(this.dumpHeapMethod, this.diagnosticMXBean, "heap", file.getAbsolutePath());
}
}
/** /**
* Exception to be thrown if the {@link HeapDumper} cannot be created. * Exception to be thrown if the {@link HeapDumper} cannot be created.
*/ */

@ -97,8 +97,9 @@ If your application is a web application (Spring MVC, Spring WebFlux, or Jersey)
| ID | Description | ID | Description
| `heapdump` | `heapdump`
| Returns an `hprof` heap dump file. | Returns a heap dump file.
Requires a HotSpot JVM. On a HotSpot JVM, an `HPROF`-format file is returned.
On an OpenJ9 JVM, a `PHD`-format file is returned.
| `jolokia` | `jolokia`
| Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux). | Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux).

Loading…
Cancel
Save