本机调用中存在多个线程(Java)

时间:2020-03-05 18:45:51  来源:igfitidea点击:

我在使用JDK 1.5.0_08的Fedora Core 6上运行的应用程序遇到问题。

在经过一定时间(通常是几天)后,线程开始陷入本机方法中。

线程被锁定在这样的东西中:

"pool-2-thread-2571" prio=1 tid=0x08dd0b28 nid=0x319e waiting for monitor entry [0xb91fe000..0xb91ff7d4]
at java.lang.Class.getDeclaredConstructors0(Native Method)

或者

"pool-2-thread-2547" prio=1 tid=0x75641620 nid=0x1745 waiting for monitor entry [0xbc7fe000..0xbc7ff554]
at sun.misc.Unsafe.defineClass(Native Method)

特别令我困惑的是:

"HealthMonitor-10" daemon prio=1 tid=0x0868d1c0 nid=0x2b72 waiting for monitor entry [0xbe5ff000..0xbe5ff4d4]
at java.lang.Thread.dumpThreads(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1383)

线程保持卡住状态,直到重新启动VM。

谁能告诉我一下这里发生了什么,什么可能导致本机方法阻塞?
每个卡住的线程顶部的监视器条目地址范围是不同的。我如何弄清楚这台显示器是什么?

任何建议或者意见将不胜感激!

谢谢,
大卫

解决方案

回答

也许我们应该使用另一个jdk版本。
对于"令人困惑的",这里有一个用于1.5.0_08的错误条目。报告内存泄漏(我不知道,这是否与问题有关):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6469701

我们也可以获取源代码并查看在1383行发生的情况。另一方面,发生原始错误后,可能只是堆栈转储。

回答

我最初的怀疑是我们正在遇到某种类加载器实现的死锁。我想,类加载需要在某种程度上进行同步,因为类信息将对整个VM可用,而不仅仅是最初加载该线程的线程。

堆栈顶部的方法是本机方法的事实似乎纯属巧合,因为部分类加载机制恰好以这种方式实现。

我将进一步研究类加载方面的明智做法。也许某些线程使用类加载器从速度较慢/不可用的网络位置加载类,从而在很长一段时间内阻塞,而不会将监视器交给要加载类的其他线程。使用-verbose:class启动JVM时调查输出可能是一件事。

回答

几个月前,我遇到了类似的问题,发现jthread(?)实用程序非常宝贵。我们为其提供Java应用程序的进程ID,它将为进程中的每个线程转储整个堆栈。

从jthread的输出中,我可以看到一个线程进入监视器后试图获取锁定,而另一个线程在获取锁后试图进入监视器。僵局的秘诀。

我还想知道应用程序是否遇到了垃圾回收问题。我们说它运行了几天才停止。我们让它处于卡住状态多长时间,以查看GC是否结束了?

回答

我们是否可以找出哪个线程实际上正在等待本机方法的监视器上进行同步?
至少在向VM发送SIGQUIT(杀死-3)时从VM获得的线程转储应该显示此信息,如下所示:

"Thread-0" prio=5 tid=0x0100b060 nid=0x84c000 waiting for monitor entry [0xb0c8a000..0xb0c8ad90]
    at Deadlock.run(Deadlock.java:8)
    - waiting to lock <0x255e5b38> (a java.lang.Object)
...
"main" prio=5 tid=0x01001350 nid=0xb0801000 waiting on condition [0xb07ff000..0xb0800148]
    at java.lang.Thread.sleep(Native Method)
    at Deadlock.main(Deadlock.java:21)
- locked <0x255e5b38> (a java.lang.Object)

在我们到目前为止发布的转储中,我看不到实际上正在等待锁定特定监视器的任何线程...