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可以为您做到这一点…(是的,在“线程”选项卡下,“检测死锁”
  • 尝试进行多次 重复的堆栈跟踪 ,以查找哪些变化与什么保持不变…
  • 对每个表示“已阻止”的堆栈条目进行取证…,然后查找特定的代码行,并确定那里是否有监视器。如果实际购买了监控器,那么确定限制资源应该相当容易。但是,如果没有透明可用的监视器,您的某些线程可能会显示为阻塞,这将更加棘手…