Java阻塞问题:为什么JVM会阻塞许多不同类/方法中的线程?
问题内容:
更新: 这看起来像是内存问题。一个3.8 Gb
Hprof文件表明,发生此“阻塞”时,JVM正在对其堆进行转储。我们的运营团队发现该站点没有响应,进行了堆栈跟踪,然后关闭了实例。我相信他们在堆转储完成之前就关闭了该站点。日志中
没有 错误/异常/问题证据-可能是因为JVM在生成错误消息之前就被杀死了。
最初的问题我们最近遇到了一种情况,该应用程序对最终用户似乎挂起了。在应用程序重新启动之前,我们获得了堆栈跟踪,我发现了一些令人惊讶的结果:在527个线程中,有463个线程的状态为“已阻塞”。
过去
过去,被阻塞的线程通常会遇到以下问题:1)一些明显的瓶颈:例如某些数据库记录锁定或文件系统锁定问题导致其他线程等待。2)所有阻塞的线程都将阻塞相同的类/方法(例如jdbc或文件系统类)
异常数据
在这种情况下,除了应用程序类(包括jdbc和lucene调用)之外,我看到各种类/方法都被阻止,包括jvm内部类,jboss类,log4j等。
问题
是什么会导致JVM阻止log4j.Hierarchy.getLogger,java.lang.reflect.Constructor.newInstance?显然,某些资源“稀缺”,但是哪个资源呢?
谢谢
将
堆栈跟踪摘录
http-0.0.0.0-80-417" daemon prio=6 tid=0x000000000f6f1800 nid=0x1a00 waiting for monitor entry [0x000000002dd5d000]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.reflect.GeneratedConstructorAccessor68.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at java.lang.Class.newInstance0(Class.java:355)
at java.lang.Class.newInstance(Class.java:308)
at org.jboss.ejb.Container.createBeanClassInstance(Container.java:630)
http-0.0.0.0-80-451" daemon prio=6 tid=0x000000000f184800 nid=0x14d4 waiting for monitor entry [0x000000003843d000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.getMethod0(Class.java:2670)
"http-0.0.0.0-80-449" daemon prio=6 tid=0x000000000f17d000 nid=0x2240 waiting for monitor entry [0x000000002fa5f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.register(Http11Protocol.java:638)
- waiting to lock <0x00000007067515e8> (a org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.createProcessor(Http11Protocol.java:630)
"http-0.0.0.0-80-439" daemon prio=6 tid=0x000000000f701800 nid=0x1ed8 waiting for monitor entry [0x000000002f35b000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.log4j.Hierarchy.getLogger(Hierarchy.java:261)
at org.apache.log4j.Hierarchy.getLogger(Hierarchy.java:242)
at org.apache.log4j.LogManager.getLogger(LogManager.java:198)
问题答案:
根据收集的证据,大致按照我尝试的顺序列出了这些内容:
- 您是否看过 GC行为 ?您是否承受着记忆压力?这可能会导致
newInstance()
上面的其他一些被阻止。运行VM-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -verbose:gc
并记录输出。在故障/锁定时间附近,您是否看到过多的GC时间?- 条件可以 重复 吗?如果是这样,请尝试在JVM(-Xmx)中使用不同的堆大小,然后查看行为是否发生了重大变化。如果是这样,请查找内存泄漏或为应用程序适当调整堆大小。
- 如果以前的操作很困难,并且您没有获得
OutOfMemoryError
应有的帮助,则可以调整GC可调参数…请参阅JDK6.0 XX选项或JDK6.0 GC Tuning Whitepaper。在专门研究-XX:+UseGCOverheadLimit
,并-XX:+GCTimeLimit
与相关的选项。(请注意,这些文件的记录不充分,但可能会有用…)
- 可能会 陷入僵局 吗?仅具有堆栈跟踪摘录,无法在此处确定。在线程被阻塞的监控器状态(相对于它们所保持的状态)中寻找周期。我相信
jconsole
可以为您做到这一点…(是的,在“线程”选项卡下,“检测死锁”) - 尝试进行多次 重复的堆栈跟踪 ,以查找哪些变化与什么保持不变…
- 对每个表示“已阻止”的堆栈条目进行取证…,然后查找特定的代码行,并确定那里是否有监视器。如果实际购买了监控器,那么确定限制资源应该相当容易。但是,如果没有透明可用的监视器,您的某些线程可能会显示为阻塞,这将更加棘手…