Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8320515

assert(monitor->object_peek() != nullptr) failed: Owned monitors should not have a dead object

    XMLWordPrintable

Details

    • b27

    Backports

      Description

        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);
        ```

        Attachments

          Issue Links

            Activity

              People

                stefank Stefan Karlsson
                stefank Stefan Karlsson
                Votes:
                0 Vote for this issue
                Watchers:
                7 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: