From 64c0eceec79b56d4c42a9b3c3449caf07ca5a21b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 30 Jul 2021 13:37:46 +0100 Subject: [PATCH] Add support for dumping the heap on OpenJ9 Closes gh-26466 --- .../src/docs/asciidoc/endpoints/heapdump.adoc | 5 +- .../management/HeapDumpWebEndpoint.java | 56 +++++++++++++++++-- .../src/docs/asciidoc/actuator/endpoints.adoc | 5 +- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc index 7695819ef1..c1e9e026c9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc @@ -7,7 +7,10 @@ The `heapdump` endpoint provides a heap dump from the application's JVM. [[heapdump.retrieving]] == Retrieving the Heap Dump 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. When using curl, this can be achieved by using the `-O` option, as shown in the following example: diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java index 51d64b258c..dd781946c7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/management/HeapDumpWebEndpoint.java @@ -100,25 +100,37 @@ public class HeapDumpWebEndpoint { if (this.heapDumper == null) { this.heapDumper = createHeapDumper(); } - File file = createTempFile(live); + File file = createTempFile(); this.heapDumper.dumpHeap(file, live); 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()); - File file = File.createTempFile("heapdump" + date + (live ? "-live" : ""), ".hprof"); + File file = File.createTempFile("heap-" + date, "." + determineDumpSuffix()); file.delete(); return file; } + private String determineDumpSuffix() { + if (this.heapDumper instanceof OpenJ9DiagnosticsMXBeanHeapDumper) { + return "phd"; + } + return "hprof"; + } + /** * Factory method used to create the {@link HeapDumper}. * @return the heap dumper to use * @throws HeapDumperUnavailableException if the heap dumper cannot be created */ protected HeapDumper createHeapDumper() throws HeapDumperUnavailableException { - return new HotSpotDiagnosticMXBeanHeapDumper(); + try { + return new HotSpotDiagnosticMXBeanHeapDumper(); + } + catch (HeapDumperUnavailableException ex) { + return new OpenJ9DiagnosticsMXBeanHeapDumper(); + } } /** @@ -140,8 +152,8 @@ public class HeapDumpWebEndpoint { } /** - * {@link HeapDumper} that uses {@code com.sun.management.HotSpotDiagnosticMXBean} - * available on Oracle and OpenJDK to dump the heap to a file. + * {@link HeapDumper} that uses {@code com.sun.management.HotSpotDiagnosticMXBean}, + * available on Oracle and OpenJDK, to dump the heap to a file. */ 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) 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. */ diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc index 6c27817e5c..3149a7c18e 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc @@ -97,8 +97,9 @@ If your application is a web application (Spring MVC, Spring WebFlux, or Jersey) | ID | Description | `heapdump` -| Returns an `hprof` heap dump file. - Requires a HotSpot JVM. +| Returns a heap dump file. + On a HotSpot JVM, an `HPROF`-format file is returned. + On an OpenJ9 JVM, a `PHD`-format file is returned. | `jolokia` | Exposes JMX beans over HTTP (when Jolokia is on the classpath, not available for WebFlux).