Fix jvmkill racing with HeapDumpOnOutOfMemoryError leading to truncated heap dumps - #9
Conversation
e47973b to
5976ca5
Compare
electrum
left a comment
There was a problem hiding this comment.
This is far too complicated. We can simply change it to only kill if the VM is out of threads.
We don't need a wall of comments. This looks like poorly generated code from AI when you don't give it any guidance. You can always ask the AI to explain the code in the future or do the analysis again.
5976ca5 to
949353a
Compare
|
The if statements are there to show the intent of only killing the JVM when resource exhaustion comes from thread allocation. The rest of the added code is for backward compatibility. I went through the airlift org docs and couldn't find any explicit advice to set -XX:+ExitOnOutOfmemoryError or -XX:+CrashOnOutOfMemoryError alongside jvmkill (correct me if I'm wrong). So there may be users who rely solely on this agent to terminate the JVM on OOM. Simply skipping the kill for non-thread OOM without checking those flags first would silently break them, leaving the JVM in an ambiguous state. The flag check is here to provide information in the logs and leave the behavior unchanged for the users without those flag set. On the implementation side the jvmti doesn't give direct access to JVM startup flags, so I'm using HotSpotDiagnosticMXBean to read them. It's the least hacky approach I found, but I'm happy to adjust or completely change it if you know of a better way or think this doesn't make sense. |
…ed heap dumps When ExitOnOutOfMemoryError or CrashOnOutOfMemoryError is set, the JDK will terminate the process itself after writing the heap dump. Killing the process unconditionally from the ResourceExhausted callback races with the JDK's dump and truncates the .hprof. Probe HotSpotDiagnosticMXBean at VM init to detect whether either exit flag is set. Skip the SIGKILL for Java heap and metaspace OOM when the JDK will already handle shutdown, allowing the dump to complete. Native thread OOM and the no-exit-flag case continue to SIGKILL as before. Fixes trinodb/trino#29301
949353a to
39ce769
Compare
Although https://trino.io/docs/current/installation/deployment.html#jvm-config includes This user empathy led to the code being complicated. HotSpotDiagnosticMXBean bean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
String value = bean.getVMOption("ExitOnOutOfMemoryError").getValue();
boolean isEnabled = Boolean.parseBoolean(value);In C, using JMVTI, it's obviously not as concise, but that's all the code does. @electrum do I read correctly that you believe we shouldn't be worried about someone running without |
Overview
jvmkillcallskill(getpid(), SIGKILL)from every JVMTIResourceExhaustedevent. On JDK 22+, this races with the JDK's own-XX:+HeapDumpOnOutOfMemoryErrorhandler and truncates the resulting.hprof.On JDK ≤ 21, the entire heap dump ran inside a single
VM_HeapDumperVM operation at safepoint. While that safepoint was held, all Java threads - including threads that lost thereport_java_out_of_memoryCAS latch and were carrying the JVMTI callback were frozen. The agent'sSIGKILLcould only fire after the dump finished and the safepoint was released, so the.hprofwas always complete on disk.JDK-8306441 (JDK 22) split the dump so that the segment-merge phase runs outside the safepoint. Loser-of-CAS hreads now unfreeze while the merge is still writing to disk, reach the JVMTI callback, and
jvmkillkills the process mid-write truncating the.hprof.Changes
Add a
VMInitcallback that probesHotSpotDiagnosticMXBeanat startup to detect whetherExitOnOutOfMemoryErrororCrashOnOutOfMemoryErroris set. When neitherExitOnOutOfMemoryErrornorCrashOnOutOfMemoryErroris set, the agent continues to SIGKILL for backward compatibility. RouteResourceExhaustedevents based on theflags argument:OOM_ERROR|JAVA_HEAP(0x3)OOM_ERROR(0x1)OOM_ERROR|THREADS(0x5)For Java heap and metaspace OOM,
report_java_out_of_memoryruns upstream of the JVMTI callback - the JDK has already started its dump and will terminate the process. Killing unconditionally races with the dump write (heapDumper.cpp) and truncates the.hprof.Native thread OOM has no JDK-side handler; SIGKILL there is unchanged.
Fixes trinodb/trino#29301