简体   繁体   English

如何诊断或检测Java静态初始化程序中的死锁

[英]How to diagnose or detect deadlocks in Java static initializers

(Whether using static initializers in Java is a good idea is out of scope for this question.) (在Java中使用静态初始化器是否是一个好主意超出了这个问题的范围。)

I am encountering deadlocks in my Scala application, which I think are caused by interlocking static initializers in the compiled classes. 我在Scala应用程序中遇到死锁,我认为这是由编译类中的互锁静态初始化器引起的。

My question is how to detect and diagnose these deadlocks -- I have found that the normal JVM tools for deadlocks do not seem to work when static initializer blocks are involved. 我的问题是如何检测和诊断这些死锁 - 我发现当涉及静态初始化程序块时,死锁的正常JVM工具似乎不起作用。

Here is a simple example Java app which deadlocks in a static initializer: 这是一个简单的Java应用程序示例,它在静态初始化程序中死锁:

public class StaticDeadlockExample implements Runnable
{
    static
    {
        Thread thread = new Thread(
                new StaticDeadlockExample(),
                "StaticDeadlockExample child thread");
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args)
    {
        System.out.println("in main");
    }

    public static void sayHello()
    {
        System.out.println("hello from thread " + Thread.currentThread().getName());
    }

    @Override
    public void run() {
        StaticDeadlockExample.sayHello();
    }
}

If you launch this app, it deadlocks. 如果你启动这个应用程序,它会死锁。 The stack trace at time of deadlock (from jstack ) contains the following two deadlocked threads: 死锁时的堆栈跟踪(来自jstack )包含以下两个死锁线程:

"StaticDeadlockExample child thread" prio=6 tid=0x000000006c86a000 nid=0x4f54 in Object.wait() [0x000000006d38f000]
   java.lang.Thread.State: RUNNABLE
    at StaticDeadlockExample.run(StaticDeadlockExample.java:37)
    at java.lang.Thread.run(Thread.java:619)

   Locked ownable synchronizers:
    - None

"main" prio=6 tid=0x00000000005db000 nid=0x2fbc in Object.wait() [0x000000000254e000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000004a6a7870> (a java.lang.Thread)
    at java.lang.Thread.join(Thread.java:1143)
    - locked <0x000000004a6a7870> (a java.lang.Thread)
    at java.lang.Thread.join(Thread.java:1196)
    at StaticDeadlockExample.<clinit>(StaticDeadlockExample.java:17)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:116)

   Locked ownable synchronizers:
    - None

My questions are as follows 我的问题如下

  1. Why is the first thread marked as RUNNABLE, when it is in fact waiting on a lock? 为什么第一个线程标记为RUNNABLE,当它实际上在等待锁定时? Can I detect the "real" state of this thread somehow? 我能以某种方式检测到这个线程的“真实”状态吗?
  2. Why is neither thread marked as owning any (relevant) locks, when in fact one holds the static intializer lock and the other is waiting for it? 为什么两个线程都没有标记为拥有任何(相关)锁,实际上一个持有静态初始化器锁而另一个正在等待它? Can I detect the static initializer lock ownership somehow? 我能以某种方式检测静态初始化程序锁定所有权吗?

Scala makes it easy to fall into the trap. Scala很容易陷入陷阱。

The easy workaround or diagnostic (if you see clinit in your stack trace) is to let your object extend App to let DelayedInit take your code off the static initializer. 简单的解决方法或诊断(如果你在堆栈跟踪中看到临床)是让你的对象扩展App让DelayedInit从静态初始化器中取出你的代码。

Some clarifying links: 一些澄清链接:

https://issues.scala-lang.org/browse/SI-7646 https://issues.scala-lang.org/browse/SI-7646

Scala: Parallel collection in object initializer causes a program to hang Scala:对象初始化程序中的并行收集导致程序挂起

http://permalink.gmane.org/gmane.comp.lang.scala.user/72499 http://permalink.gmane.org/gmane.comp.lang.scala.user/72499

I have tried this example with my tool and it also fails in detecting this as a deadlock. 我用我的工具尝试了这个例子,它也无法将其检测为死锁。 After struggling a bit with the jconsole debugger and re-running the example a couple of times I noticed that the initial thread is marked as RUNNABLE because it is runnable, the problem here is that since the launched thread access to a static member, this operation is queued after the finishing of the static initializer block (this semantic is not clear in the JVM specification , however it seems to be the case). 在与jconsole调试器稍作挣扎并重新运行该示例几次后,我注意到初始线程被标记为RUNNABLE,因为它是可运行的,这里的问题是,由于启动的线程访问静态成员,此操作在完成静态初始化程序块之后排队(这种语义在JVM规范中并不清楚,但似乎是这种情况)。

The static initializer does not finish because in this bizarre example a join operation forces it to wait for the thread termination, however I remark that this "queued" operation does not grab a lock explicitly nor implicitly according to the JVM specification. 静态初始化程序没有完成,因为在这个奇怪的例子中,连接操作强制它等待线程终止,但是我注意到这个“排队”操作不会根据JVM规范明确地或隐式地获取锁。 Perhaps this shouldn't be considered a deadlock per se as it would be the same case if the body of the run method contains an infinite loop. 也许这不应该被认为是死锁本身,因为如果run方法的主体包含无限循环,情况也是如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM