-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
8
-
generic
-
generic
JVM may crash with "ExceptionMark destructor expects no pending exceptions" error.
Some code uses the following ExceptionMark to verify that none of the exceptions are pending.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/utilities/exceptions.cpp#L465
ExceptionMark::~ExceptionMark() {
if (_thread->has_pending_exception()) {
Handle exception(_thread, _thread->pending_exception());
_thread->clear_pending_exception(); // Needed to avoid infinite recursion
if (is_init_completed()) {
exception->print();
fatal("ExceptionMark destructor expects no pending exceptions");
} else {
vm_exit_during_initialization(exception);
}
}
}
For example, if OutOfMemoryError occurs during VM initialization, you may experience a crash as follows:
java.lang.OutOfMemoryError
- klass: 'java/lang/OutOfMemoryError'
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (exceptions.cpp:471), pid=12576, tid=0x0000000000000ee0
# fatal error: ExceptionMark destructor expects no pending exceptions
#
# JRE version: OpenJDK Runtime Environment (8.0_402-b06) (build 1.8.0_402-b06)
# Java VM: OpenJDK 64-Bit Server VM (25.402-b06 mixed mode windows-amd64 )
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# F:\tmp\hs_err_pid12576.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/adoptium/adoptium-support/issues
#
The instance of ExceptionMark is created and is_init_completed() is called in Threads::create_vm. Therefore, the ExceptionMark destructor will cause a fatal error if the pending exception remains at the end of Threads:: create_vm. In particular, if OutOfMemoryError is thrown, it seems to remain as a pending exception.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/runtime/thread.cpp
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
...
EXCEPTION_MARK
...
set_init_completed();
...
}
In the latest JDK, create_vm() does not use ExceptionMark.
How to reproduce:
When reproducing the crash, it is better to disable Java Compressed Class Pointers because the other validation for MaxMetaspaceSize will throw another exception before ExceptionMark destructor.
Set MaxMetaspaceSize to a small value.
On Windows 10, I can reproduce it with the following command line:
openjdk8/bin/java -XX:-UseCompressedOops -XX:MaxMetaspaceSize=2500k -version
Other code could potentially have the same problem.
The ExceptionMark used in the following code may cause a crash.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/c1/c1_Runtime1.cpp#L684
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/interpreter/oopMapCache.cpp#L364
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/memory/filemap.cpp#L235
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/memory/universe.cpp#L502
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/oops/klass.cpp#L564
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/runtime/sharedRuntime.cpp#L1916
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/services/management.cpp#L1828
These codes that use ExceptionMark do not exist in the latest JDK, but there may be others that do.
Some code uses the following ExceptionMark to verify that none of the exceptions are pending.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/utilities/exceptions.cpp#L465
ExceptionMark::~ExceptionMark() {
if (_thread->has_pending_exception()) {
Handle exception(_thread, _thread->pending_exception());
_thread->clear_pending_exception(); // Needed to avoid infinite recursion
if (is_init_completed()) {
exception->print();
fatal("ExceptionMark destructor expects no pending exceptions");
} else {
vm_exit_during_initialization(exception);
}
}
}
For example, if OutOfMemoryError occurs during VM initialization, you may experience a crash as follows:
java.lang.OutOfMemoryError
- klass: 'java/lang/OutOfMemoryError'
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (exceptions.cpp:471), pid=12576, tid=0x0000000000000ee0
# fatal error: ExceptionMark destructor expects no pending exceptions
#
# JRE version: OpenJDK Runtime Environment (8.0_402-b06) (build 1.8.0_402-b06)
# Java VM: OpenJDK 64-Bit Server VM (25.402-b06 mixed mode windows-amd64 )
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# F:\tmp\hs_err_pid12576.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/adoptium/adoptium-support/issues
#
The instance of ExceptionMark is created and is_init_completed() is called in Threads::create_vm. Therefore, the ExceptionMark destructor will cause a fatal error if the pending exception remains at the end of Threads:: create_vm. In particular, if OutOfMemoryError is thrown, it seems to remain as a pending exception.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/runtime/thread.cpp
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
...
EXCEPTION_MARK
...
set_init_completed();
...
}
In the latest JDK, create_vm() does not use ExceptionMark.
How to reproduce:
When reproducing the crash, it is better to disable Java Compressed Class Pointers because the other validation for MaxMetaspaceSize will throw another exception before ExceptionMark destructor.
Set MaxMetaspaceSize to a small value.
On Windows 10, I can reproduce it with the following command line:
openjdk8/bin/java -XX:-UseCompressedOops -XX:MaxMetaspaceSize=2500k -version
Other code could potentially have the same problem.
The ExceptionMark used in the following code may cause a crash.
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/c1/c1_Runtime1.cpp#L684
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/interpreter/oopMapCache.cpp#L364
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/memory/filemap.cpp#L235
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/memory/universe.cpp#L502
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/oops/klass.cpp#L564
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/runtime/sharedRuntime.cpp#L1916
https://github.com/openjdk/jdk8u-dev/blob/1fad52dc04938f9aae3fa372632011bcaf80b265/hotspot/src/share/vm/services/management.cpp#L1828
These codes that use ExceptionMark do not exist in the latest JDK, but there may be others that do.