JDK-6380127 : findMonitorDeadlockedThreads() hangs during Event-Main thread deadlock
  • Type: Bug
  • Component: hotspot
  • Sub-Component: svc
  • Affected Version: 5.0
  • Priority: P5
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-02-02
  • Updated: 2023-12-13
  • Resolved: 2016-11-04
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.
Other
tbdResolved
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
Java 1.5.0_06 with Sun Microsystems Inc. Java HotSpot(TM) Client VM mixed mode

ADDITIONAL OS VERSION INFORMATION :
Windows XP

A DESCRIPTION OF THE PROBLEM :
I would like to use findMonitorDeadlockedThreads() to determine when a thread lock occurs in my application. I can get this working fine for generic thread deadlocks, but I can't get this working if the deadlock is between the Main thread and the Event Dispatch thread. This is the most common deadlock scenario for Java apps, so it is surprising that the management API is not robust.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :


  To reproduce:
ThreadMonitor.runme("mylogfile.txt");

Create a generic deadlock
ThreadMonitor.testDeadLock(); // works okay

Create a swing deadlock:
ThreadMonitor.runme("mylogfile.txt");
StrangeProblem(); // doesn't work

No log file is written.



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would expect fMBean.findMonitorDeadlockedThreads() to be robust to AWT-Main deadlocks
ACTUAL -
The thread that calls findMonitorDeadlockedThreads() hangs inside this method call.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
>>>>>>>>>>>>>>>>>>>>>>>>>.
import java.lang.management.*;
import java.util.*;
import java.io.FileWriter;
import java.io.BufferedWriter;

public class ThreadMonitor extends Thread {
    private static final int FREQUENCY = 5000;  // every 5 seconds
    private Set fDeadlockedThreads = new HashSet();
    private String fFilename;
   // private final Timer fThreadCheck = new Timer("Thread Monitor", true);


    public static void runme(String filename) {
        ThreadMonitor t = new ThreadMonitor(filename);
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();
    }
    private ThreadMonitor(String filename) {
        fFilename = filename;
    }

    /**
     * Monitor deadlocks.
     */
    public void run() {

        ThreadMXBean fMBean = ManagementFactory.getThreadMXBean();
        boolean flag = false;
        while(!flag) {
                BufferedWriter out = null;
                try {
                                //Thread.sleep(5000);


                    //System.out.println("checking...");
                    long[] ids = fMBean.findMonitorDeadlockedThreads();
                    if (ids != null && ids.length > 0) {
                        //System.out.println("Thread deadlock detected, see log: " + fFilename);
                        FileWriter fw = new FileWriter(fFilename);
                        out = new BufferedWriter(fw);
                        for (int n = 0; n < ids.length; n++) {
                            ThreadInfo ti = fMBean.getThreadInfo(ids[n], 20);
                            if (!fDeadlockedThreads.contains(new Long(ids[n]))) {
                                StackTraceElement el[] = ti.getStackTrace();
                                if (out!=null) {
                                    out.write("Thread lock for: " + ti.getThreadName());
                                    out.newLine();
                                    for(int i = 0; i<el.length; i++) {
                                        out.write(el[i].getClassName()+ "."+el[i].getMethodName());
                                        out.newLine();
                                    }
                                    out.write("");
                                    out.newLine();
                                }
                                fDeadlockedThreads.add(new Long(ids[n]));
                            }
                        }
                        out.close();
                        flag = true;

                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }


    // got this from internet
    public static void testDeadLock() {
        // deadlock with three locks
        Object lock1 = new String("lock1");
        Object lock2 = new String("lock2");
        Object lock3 = new String("lock3");

        new DeadlockingThread("t1", lock1, lock2);
        new DeadlockingThread("t2", lock2, lock3);
        new DeadlockingThread("t3", lock3, lock1);
    }

    private static class DeadlockingThread extends Thread {
        private final Object lock1;
        private final Object lock2;

        public DeadlockingThread(String name, Object lock1, Object lock2) {
            super(name);
            this.lock1 = lock1;
            this.lock2 = lock2;
            start();
        }
        public void run() {
            while (true) {
                f();
            }
        }
        private void f() {
            synchronized (lock1) {
                g();
            }
        }
        private void g() {
            synchronized (lock2) {
                // do some work...
                for (int i = 0; i < 1000 * 1000; i++) ;
            }
        }
    }
}


>>>>>>>>>>>>>>>>>>>>>>>>.
import javax.swing.*;
import java.awt.*;

public class StrangeProblem extends JFrame {
  static {
    new StrangeProblem();
  }

  private static void staticMethod() {
    System.out.println("This is never reached");
  }

  private StrangeProblem() {
    getContentPane().add(new MyJDesktopPane());
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(300, 300);
    setVisible(true);
    // If commented out, program works fine, otherwise it hangs
    new JInternalFrame();
  }

  private class MyJDesktopPane extends JDesktopPane {
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      System.out.println("We will now call the static method...");
      staticMethod();
      System.out.println("Static method was called.");
    }
  }

  public static void main(String[] args) {
  }
}

---------- END SOURCE ----------

Comments
Based on David's comment we're giving this back to svc
24-03-2014

When class initialization is in progress by Thread A and Thread B needs to access the class in a way that requires initialization, then Thread B must wait until Thread A has finished the initialization. This is done by creating an ObjectLocker for an oop that represents the class initialization lock (the identity of which has changed over time - as of permgen removal it is an int[] created for that purpose) and waiting on the associated ObjectMonitor: instanceKlass.cpp: // refer to the JVM book page 47 for description of steps // Step 1 { volatile oop init_lock = this_oop->init_lock(); ObjectLocker ol(init_lock, THREAD); Thread *self = THREAD; // it's passed the current thread // Step 2 // If we were to use wait() instead of waitInterruptibly() then // we might end up throwing IE from link/symbol resolution sites // that aren't expected to throw. This would wreak havoc. See 6320309. while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { wait = true; ol.waitUninterruptibly(CHECK); } The ObjectMonitor will set the thread's VM state but the JavaThreadState is handled at a higher level. In particular JVM_MonitorWait in jvm.cpp is where it happens for Object.wait(): JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) JVMWrapper("JVM_MonitorWait"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); JavaThreadInObjectWaitState jtiows(thread, ms != 0); if (JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); } ObjectSynchronizer::wait(obj, ms, CHECK); JVM_END It may be possible to add a JavaThreadInObjectWaitState to the instanceKLass code but be aware that we do an uninterruptible wait - something not visible/known at the Java level (waits are always interruptible), and we need to beware of exposing the lock object (which in the past was not even a Java-level object). Note further that we do not perform any JVMTI event processing around this particular "wait" and in general we do not try to maintain 100% correlation between Java thread state and VM thread state. Further the Java thread states relate to actions performed at the Java-level and we are not performing the wait at that level. It is commonly the case that the Java thread state is RUNNING but the VM thread state is actually blocked due to internal lock acquisition etc. So the right solution here might be to make the management API more aware of the VM thread state.
15-08-2013

The <clinit> lock stack trace
14-08-2013

This has nothing to do with the deadlock detector or event thread. The lock you experience is due to the static initializer not having finished when you try to invoke a static method. Unfortunately, the thread is reported as RUNNING while it is waiting for the static initializer to finish its job. Main thread: StrangeProblem.<clinit> -> StrangeProblem.<init> -> JInternalFrame.<init> {wait for AWTTree lock} ... // thread reported as RUNNING Event thread: [event loop] -> {AWTTree lock :: locked} -> StrangeProblem$MyJDesktopPane.paint() -> StrangeProblem.staticMethod() {waiting for StrangeProblem.<clinit> to finish} ... // thread reported as BLOCKED This seems to be a more general problem of a thread being reported as RUNNING while it is in fact waiting for <clinit> to finish before accessing static members. I am re-assigning the issue to hotspot/runtime for further evaluation.
14-08-2013

EVALUATION The deadlock detection algorithm used by the java.lang.management API is same as the one in the control-break (or sigquit) handler built-in in the VM. It checks for threads that are blocked to enter a monitor. However, AWT-EventQueue-0 thread is not reported to be blocked to enter a monitor.
08-02-2006