JDK-8274136 : -XX:+ExitOnOutOfMemoryError calls exit while threads are running
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 18
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-09-22
  • Updated: 2021-10-04
  • Resolved: 2021-09-28
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 18
18 b17Fixed
Related Reports
Relates :  
Relates :  
Description
In JDK-8274072 we found that JDWP forcefully calls exit, which will run the destructors of global variables. This is problematic because other threads, like the GC threads, could still be using those objects.

We have a similar problem with -XX:+ExitOnOutOfMemoryError. It calls os::exit(), and causes the same problem.

I can reproduce this issue by adding this patch:
---
$ git diff
diff --git a/src/hotspot/share/gc/shared/gcTimer.cpp b/src/hotspot/share/gc/shared/gcTimer.cpp
index ca835e30ed3..150e03be0af 100644
--- a/src/hotspot/share/gc/shared/gcTimer.cpp
+++ b/src/hotspot/share/gc/shared/gcTimer.cpp
@@ -117,9 +117,16 @@ TimePartitions::TimePartitions() {
   clear();
 }
 
+#include "runtime/os.hpp"
+#include "runtime/globals.hpp"
 TimePartitions::~TimePartitions() {
   delete _phases;
   _phases = NULL;
+  if (UseNewCode) {
+  fprintf(stderr, "~TimePartitions sleep\n");
+  os::naked_short_sleep(999);
+  fprintf(stderr, "~TimePartitions sleep done\n");
+  }
 }
 
 void TimePartitions::clear() {
---

and then running, say, SPECjbb2005:
java -XX:+UseZGC -XX:+UseNewCode -Xlog:gc -Xmx32m -Xms32m -XX:+ExitOnOutOfMemoryError -cp jbb.jar:check.jar spec.jbb.JBBmain

which results in:
Terminating due to java.lang.OutOfMemoryError: Java heap space
~TimePartitions sleep
...
# A fatal error has been detected by the Java Runtime Environment:
...
# V  [libjvm.so+0xd06b08]  GCTimer::register_gc_start(TimeInstant<CompositeCounterRepresentation, CompositeElapsedCounterSource> const&)+0x8

Comments
Changeset: 2657bcbd Author: David Holmes <dholmes@openjdk.org> Date: 2021-09-28 23:24:23 +0000 URL: https://git.openjdk.java.net/jdk/commit/2657bcbd9965d8af83f4063e3602c409735493d1
28-09-2021

ILW = MLM = P4
28-09-2021

I decided for simplicity and clarity to just add os::_exit() to call _exit().
23-09-2021

I went back through JDK-8138745 (which introduced these flags) to try and get a sense of expectations. The primary motivation was to make it easier to do "--XX:OnOutOfMemoryError=`kill -9 %p`" (which is problematic due to quoting issues when passed through various init files and scripts), which suggests an expectation of immediate termination with no cleanup. The actual API provided ExitOn... and CrashOn... because that is what Jrockit had provided (and the documention there doesn't clearly set expectations either). I can't find any bug reports that might expose any specific expectations, nor does an internet search provide any insight. Based on the above I will create a PR to call _exit() instead and we shall see if anyone else has specific expectations here. Actually arranging the call to _exit() is itself not so simple due to the plethora of existing termination methods: os::exit(), os::abort() and os::die() - none of which are exactly what we want on all platforms. os::die() is exactly what we want for Windows, but not Posix (which calls ::abort and core dumps). So we need to flag yet-another-special-case - probably in os::die(). Aside: another oddity/inconsistency in all this is that os::abort() calls os::shutdown() while os::exit() does not.
23-09-2021

From reading this: https://github.com/eclipse-openj9/openj9/issues/8552 it appears that OpenJ9 actually does an orderly shutdown in response to ExitOnOutOfMemoryError.
23-09-2021

Also note that while running at_exit handlers etc may be a problem for the VM, it is possible that applications using this flag actually install their own at_exit handlers and expect them to run in this case.
23-09-2021

Given we have both ExitOnOutOfMemoryError and CrashOnOutOfMemoryError, it does seem like ExitOnOutOfMemoryError should be performing a more controlled/orderly "exit". The question is: should that be an actual orderly VM shutdown that runs Java exit hooks etc? Or should it just be a close as possible to an abort with no coredump? Presently it is the latter but not as close as it could be if we used _exit() instead of exit() - avoiding the running of at_exit handlers and global destructors.
23-09-2021