-
Notifications
You must be signed in to change notification settings - Fork 41.2k
Allow heap dump in native image with Spring Boot Actuator #36165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for the suggestion, @cmdjulian. I think this is something that we could support out of the box. In the meantime, you can enable it yourself by adding something like the following to your app: package com.example.demo;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.boot.actuate.management.HeapDumpWebEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.NativeDetector;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import com.example.demo.GraalHeapDumpWebEndpointConfiguration.GraalHeapDumpWebEndpoint.GraalHeapDumper;
import com.example.demo.GraalHeapDumpWebEndpointConfiguration.GraalHeapDumperRuntimeHints;
@Configuration(proxyBeanMethods = false)
@ImportRuntimeHints(GraalHeapDumperRuntimeHints.class)
class GraalHeapDumpWebEndpointConfiguration {
@Bean
HeapDumpWebEndpoint heapDumpWebEndpoint() {
return new GraalHeapDumpWebEndpoint();
}
static class GraalHeapDumpWebEndpoint extends HeapDumpWebEndpoint {
@Override
protected HeapDumper createHeapDumper() throws HeapDumperUnavailableException {
if (NativeDetector.inNativeImage()) {
return new GraalHeapDumper();
}
return super.createHeapDumper();
}
static class GraalHeapDumper implements HeapDumper {
private static final String VM_RUNTIME_CLASS_NAME = "org.graalvm.nativeimage.VMRuntime";
private final Method dumpHeap;
GraalHeapDumper() {
try {
Class<?> vmRuntimeClass = ClassUtils.resolveClassName(GraalHeapDumper.VM_RUNTIME_CLASS_NAME, null);
this.dumpHeap = vmRuntimeClass.getMethod("dumpHeap", String.class, boolean.class);
}
catch (Throwable ex) {
throw new HeapDumperUnavailableException("Cound not find dumpHeap method on VMRuntime", ex);
}
}
@Override
public File dumpHeap(Boolean live) throws IOException {
String date = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm").format(LocalDateTime.now());
File file = File.createTempFile("heap-" + date, ".hprof");
ReflectionUtils.invokeMethod(this.dumpHeap, null, file.getAbsolutePath(), (live != null) ? live : true);
return file;
}
}
}
static class GraalHeapDumperRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(TypeReference.of(GraalHeapDumper.VM_RUNTIME_CLASS_NAME),
MemberCategory.INVOKE_PUBLIC_METHODS);
}
}
} |
Cool, thanks for your help! I will check it out later :) |
Please see #31680 and the Graal issue to which it links. |
For the sake of completeness, I added the following VM options to my build gradle file: @Configuration(proxyBeanMethods = false)
@ImportRuntimeHints(GraalHeapDumperRuntimeHints::class)
internal class GraalHeapDumpWebEndpointConfiguration {
@Bean
@ConditionalOnAvailableEndpoint(endpoint = HeapDumpWebEndpoint::class)
fun heapDumpWebEndpoint(): HeapDumpWebEndpoint = GraalHeapDumpWebEndpoint()
}
object GraalHeapDumperRuntimeHints : RuntimeHintsRegistrar {
override fun registerHints(hints: RuntimeHints, classLoader: ClassLoader?) {
hints.reflection()
.registerType(TypeReference.of(GraalHeapDumper.VM_RUNTIME_CLASS_NAME), MemberCategory.INVOKE_PUBLIC_METHODS)
}
}
private class GraalHeapDumpWebEndpoint : HeapDumpWebEndpoint() {
object GraalHeapDumper : HeapDumper {
const val VM_RUNTIME_CLASS_NAME = "org.graalvm.nativeimage.VMRuntime"
private val dumpHeap = try {
val vmRuntimeClass = ClassUtils.resolveClassName(VM_RUNTIME_CLASS_NAME, null)
vmRuntimeClass.getMethod("dumpHeap", String::class.java, Boolean::class.javaPrimitiveType)
} catch (ex: Throwable) {
throw HeapDumperUnavailableException("Could not find dumpHeap method on VMRuntime", ex)
}
override fun dumpHeap(live: Boolean?): File {
val date = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm").format(LocalDateTime.now())
val heapDumpPath = createTempFile("heap-$date", ".hprof")
ReflectionUtils.invokeMethod(dumpHeap, null, heapDumpPath.pathString, live ?: true)
return heapDumpPath.toFile()
}
}
override fun createHeapDumper(): HeapDumper =
if (NativeDetector.inNativeImage()) GraalHeapDumper else super.createHeapDumper()
} It got me a little by surprise that |
Spring Boot Actuator is a tremendous help, especially when debugging prod issues.
Especially heap dump and thread profiling are very helpful.
This works very conveniently when running in JVM mode. When running in graalvm native image, this two features don't work.
I stumbled up on https://www.graalvm.org/latest/reference-manual/native-image/guides/create-heap-dump/ this graalvm feature and was wondering if we can't use this to allow heap dumps to work in native image as well by using the in-native-image detector and than run the example code conditionally.
The text was updated successfully, but these errors were encountered: