JDK 21 | JDK 22 |
---|---|
21.0.4-oracleFixed | 22 b27Fixed |
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
We hit this assert when JNI code calls MonitorEnter, makes the object unreachable, and then detaches the thread without calling MonitorExit. This can be seen by running the CompleteExit test with `-XX:+UseZGC -Xmx12m` and the following changes: ``` diff --git a/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c b/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c index 07ba0ff0ef8..fac500542ea 100644 --- a/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c +++ b/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c @@ -44,8 +44,27 @@ static void* do_test() { int res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); if (res != JNI_OK) die("AttachCurrentThread"); - if ((*env)->MonitorEnter(env, t1) != 0) die("MonitorEnter"); - if ((*env)->MonitorEnter(env, t2) != 0) die("MonitorEnter"); + jclass c = (*env)->FindClass(env, "java/lang/Object"); + if (c == 0) { + die("No class"); + } + + jmethodID m = (*env)->GetMethodID(env, c, "<init>", "()V"); + if (m == 0) { + die("No constructor"); + } + + jobject o1 = (*env)->NewObject(env, c, m); + jobject o2 = (*env)->NewObject(env, c, m); + jobject o3 = (*env)->NewObject(env, c, m); + + if ((*env)->MonitorEnter(env, o1) != 0) die("MonitorEnter"); + if ((*env)->MonitorEnter(env, o2) != 0) die("MonitorEnter"); + + (*env)->DeleteLocalRef(env, o1); + (*env)->DeleteLocalRef(env, o2); + + (*env)->NewObjectArray(env, 1024 * 1024, c, o3); if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) die("DetachCurrentThread"); pthread_exit(NULL); ``` The assert comes from this path: V [libjvm.so+0x171fb1d] ObjectSynchronizer::owned_monitors_iterate(MonitorClosure*, JavaThread*)+0x15d (synchronizer.cpp:1094) V [libjvm.so+0x1720d2a] ObjectSynchronizer::release_monitors_owned_by_thread(JavaThread*)+0x4a (synchronizer.cpp:1792) V [libjvm.so+0xeb806c] JavaThread::exit(bool, JavaThread::ExitType)+0x7fc (javaThread.cpp:871) V [libjvm.so+0xf8066a] jni_DetachCurrentThread+0xda (jni.cpp:3923) C [libCompleteExit.so+0xb53] do_test+0x173 (libCompleteExit.c:73) I've also managed to reproduce this issue from VM_ThreadDump with this patch: ``` diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index ed323b7a416..7bf8091a4e6 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -1091,7 +1091,7 @@ void ObjectSynchronizer::owned_monitors_iterate_filtered(MonitorClosure* closure // ObjectMonitor cannot be async deflated. if (monitor->has_owner() && filter(monitor->owner_raw())) { assert(!monitor->is_being_async_deflated(), "Owned monitors should not be deflating"); - assert(monitor->object_peek() != nullptr, "Owned monitors should not have a dead object"); + assert(UseNewCode || monitor->object_peek() != nullptr, "Owned monitors should not have a dead object"); closure->do_monitor(monitor); } diff --git a/test/hotspot/jtreg/runtime/Monitor/CompleteExit.java b/test/hotspot/jtreg/runtime/Monitor/CompleteExit.java index 7e63050dc9e..11b6149978d 100644 --- a/test/hotspot/jtreg/runtime/Monitor/CompleteExit.java +++ b/test/hotspot/jtreg/runtime/Monitor/CompleteExit.java @@ -31,6 +31,9 @@ * @run main/native CompleteExit */ +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; + public class CompleteExit { public static native void testIt(Object o1, Object o2); @@ -41,7 +44,21 @@ public class CompleteExit { System.loadLibrary("CompleteExit"); } + static private void dumpThreads() { + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + while (true) { + threadBean.dumpAllThreads(true, false); + try { + Thread.sleep(1); + } catch (InterruptedException e) {} + } + } + public static void main(String[] args) throws Exception { + Thread threadDumper = new Thread(() -> dumpThreads()); + threadDumper.setDaemon(true); + threadDumper.start(); + testIt(o1, o2); } } diff --git a/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c b/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c index 07ba0ff0ef8..6b1a5465b2e 100644 --- a/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c +++ b/test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <pthread.h> #include <stdio.h> +#include <unistd.h> #define die(x) do { printf("%s:%s\n",x , __func__); perror(x); exit(EXIT_FAILURE); } while (0) @@ -44,8 +45,30 @@ static void* do_test() { int res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); if (res != JNI_OK) die("AttachCurrentThread"); - if ((*env)->MonitorEnter(env, t1) != 0) die("MonitorEnter"); - if ((*env)->MonitorEnter(env, t2) != 0) die("MonitorEnter"); + jclass c = (*env)->FindClass(env, "java/lang/Object"); + if (c == 0) { + die("No class"); + } + + jmethodID m = (*env)->GetMethodID(env, c, "<init>", "()V"); + if (m == 0) { + die("No constructor"); + } + + jobject o1 = (*env)->NewObject(env, c, m); + jobject o2 = (*env)->NewObject(env, c, m); + jobject o3 = (*env)->NewObject(env, c, m); + + if ((*env)->MonitorEnter(env, o1) != 0) die("MonitorEnter"); + if ((*env)->MonitorEnter(env, o2) != 0) die("MonitorEnter"); + + (*env)->DeleteLocalRef(env, o1); + (*env)->DeleteLocalRef(env, o2); + + (*env)->NewObjectArray(env, 1024 * 1024, c, o3); + printf("Going to sleep: 10s"); + sleep(10); + printf("Slept"); if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) die("DetachCurrentThread"); pthread_exit(NULL); ```
|