Java线程转储:没有“等待锁定……”的BLOCKED线程

我在理解我从jstack获得的线程转储有困难,因为在Tomcat 6上运行的Spring MVC Web应用程序(java 1.6.0_22,Linux)。

我看到阻塞线程(导致其他线程等待)自己被阻塞,但是线程转储没有告诉我为什么或者他们正在等待哪个监视器。

例:

"TP-Processor75" daemon prio=10 tid=0x00007f3e88448800 nid=0x56f5 waiting for monitor entry [0x00000000472bc000] java.lang.Thread.State: BLOCKED (on object monitor) at java.lang.Class.initAnnotationsIfNecessary(Class.java:3067) - locked  (a java.lang.Class for org.catapultframework.resource.ResourceObject) at java.lang.Class.getAnnotation(Class.java:3029) ... 

即我错过了堆栈跟踪中的“等待锁定…”行。 显然线程锁定了一个Class对象,但是我不明白为什么线程本身被阻塞了。

线程转储不包含任何死锁提示。

如何识别锁定监视器?

谢谢,奥利弗

显然,我们观察到这些被阻塞的线程的情况与大量内存消耗和大量垃圾收集有关。

这个问题Java阻塞问题:为什么JVM会在许多不同的类/方法中阻塞线程? 描述了类似的情况,所以我相信这些线程只是被垃圾收集器阻止了。

(无论如何,在解决了内存问题之后,阻塞线程的这个问题就消失了。)

检查终结器线程是否被阻塞或等待。

在GC扫描期间,GC将“停止世界”以执行其清理。 “世界”的定义取决于所使用的垃圾收集器和上下文。 它可能是一小组线程或所有线程。 在正式收集垃圾之前,GC将调用对象的finalize()。

如果您处于执行终结器方法的不良情况,则终结代码可能阻止其完成并且“世界”保持停止。

当看到许multithreading被一些未知的魔力永久阻挡时,这是最明显的:查找阻塞发生的代码并且没有任何意义; 在它附近的任何地方都没有阻塞代码,并且转储不会泄露它正在等待的监视器,因为没有阻塞代码。 GC暂停了线程。

我刚才在Google Chrome中使用Applet时遇到了类似的问题。

简而言之:

  • 当VM需要加载类时,可以阻止BLOCKED线程。
  • 当加载类本身的过程被某些东西阻止时,可能会发生整个应用程序的冻结。

详细:

我有以下场景:

  1. 我在Chrome中使用了一个带有codebase =文件夹的Applet到单个类文件(没有jar)
  2. 该网站使用LiveConnect将焦点事件传递给applet
  3. 传入的JS调用正在使用带有new Runnable() ...Executor来分离调用以减少等待时间,从而在JS中挂起。
  4. 这就是问题发生的地方!

说明:

  • new Runnable()是一个匿名的内部类,在JS调用发生之前没有加载。
  • 因此,JS调用会触发类加载。
  • 但现在类加载器被阻止了,因为它需要通过处理传入JS调用的相同队列或机制与浏览器(我猜)对话。

以下是尝试加载类的阻塞线程:

 "Thread-20" daemon prio=4 tid=0x052e8400 nid=0x4608 in Object.wait() [0x0975d000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) at sun.plugin2.message.Queue.waitForMessage(Unknown Source) - locked <0x29fbc5d8> (a sun.plugin2.message.Queue) at sun.plugin2.message.Pipe$2.run(Unknown Source) at com.sun.deploy.util.Waiter$1.wait(Unknown Source) at com.sun.deploy.util.Waiter.runAndWait(Unknown Source) at sun.plugin2.message.Pipe.receive(Unknown Source) at sun.plugin2.main.client.MessagePassingExecutionContext.doCookieOp(Unknown Source) at sun.plugin2.main.client.MessagePassingExecutionContext.getCookie(Unknown Source) at sun.plugin2.main.client.PluginCookieSelector.getCookieFromBrowser(Unknown Source) at com.sun.deploy.net.cookie.DeployCookieSelector.getCookieInfo(Unknown Source) at com.sun.deploy.net.cookie.DeployCookieSelector.get(Unknown Source) - locked <0x298da868> (a sun.plugin2.main.client.PluginCookieSelector) at sun.net.www.protocol.http.HttpURLConnection.setCookieHeader(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.writeRequests(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) - locked <0x2457cdc0> (a sun.net.www.protocol.http.HttpURLConnection) at com.sun.deploy.net.HttpUtils.followRedirects(Unknown Source) at com.sun.deploy.net.BasicHttpRequest.doRequest(Unknown Source) at com.sun.deploy.net.BasicHttpRequest.doGetRequestEX(Unknown Source) at com.sun.deploy.cache.ResourceProviderImpl.checkUpdateAvailable(Unknown Source) at com.sun.deploy.cache.ResourceProviderImpl.isUpdateAvailable(Unknown Source) at com.sun.deploy.cache.DeployCacheHandler.get(Unknown Source) - locked <0x245727a0> (a java.lang.Object) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) - locked <0x24572020> (a sun.net.www.protocol.http.HttpURLConnection) at java.net.HttpURLConnection.getResponseCode(Unknown Source) at sun.plugin2.applet.Applet2ClassLoader.getBytes(Unknown Source) at sun.plugin2.applet.Applet2ClassLoader.access$000(Unknown Source) at sun.plugin2.applet.Applet2ClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at sun.plugin2.applet.Applet2ClassLoader.findClass(Unknown Source) at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source) at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source) - locked <0x299726b8> (a sun.plugin2.applet.Applet2ClassLoader) at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source) - locked <0x299726b8> (a sun.plugin2.applet.Applet2ClassLoader) at java.lang.ClassLoader.loadClass(Unknown Source) 

正如您所看到的那样,它正在等待消息 – > waitForMessage()

同时我们传入的JS调用在这里被阻塞:

 "Applet 1 LiveConnect Worker Thread" prio=4 tid=0x05231800 nid=0x1278 waiting for monitor entry [0x0770e000] java.lang.Thread.State: BLOCKED (on object monitor) at MyClass.myMethod(MyClass.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.plugin.javascript.Trampoline.invoke(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.plugin.javascript.JSClassLoader.invoke(Unknown Source) at sun.plugin2.liveconnect.JavaClass$MethodInfo.invoke(Unknown Source) at sun.plugin2.liveconnect.JavaClass$MemberBundle.invoke(Unknown Source) at sun.plugin2.liveconnect.JavaClass.invoke0(Unknown Source) at sun.plugin2.liveconnect.JavaClass.invoke(Unknown Source) at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$DefaultInvocationDelegate.invoke(Unknown Source) at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$3.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo.doObjectOp(Unknown Source) at sun.plugin2.main.client.LiveConnectSupport$PerAppletInfo$LiveConnectWorker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) 

其他其他线程以相同方式被阻止。 我想所有后续的类加载请求都被第一个被阻止的类加载线程阻止了。

如前所述,我的猜测是类挂载进程被挂起的JS调用阻塞,该调用本身被要加载的缺失类阻塞。

解决方案:

  1. 在从JS进行任何调用之前,触发器在applet的构造函数中加载所有相关的类。
  2. 如果没有单独加载类文件,而是从jar文件加载它可能会有所帮助。 这背后的理论是:类加载器不需要与浏览器通信来从jar文件加载类(这将是
  3. 与1结合使用:使用动态代理类来包装所有传入的JS调用,并在Executor中独立运行它们。

我对#3的实现:

 public class MyClass implements JsCallInterface { private final JsCallInterface jsProxy; private final static interface JsCallInterface { public void myMethod1Intern(String param1, String param2); } private final class JsCallRunnable implements Runnable { private final Method method; private final Object[] args; private JsCallRunnable(Method method, Object[] args) { this.method = method; this.args = args; } public void run() { try { method.invoke(MyClass.this, args); } catch (Exception e) { e.printStackTrace(); } } } public MyClass() { MyUtilsClass.class.getName(); // load class JsCallRunnable.class.getName(); // load class InvocationHandler jsCallHandler = new InvocationHandler() { public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { MyUtilsClass.executeInExecutor(new JsCallRunnable(method, args)); return null; } }; jsProxy = (JsCallInterface) Proxy.newProxyInstance(MyClass.class.getClassLoader(), new Class[] { JsCallInterface.class }, jsCallHandler); } public void myMethod1(String param1, String param2) { jsProxy.myMethod1Intern(param1, param2); // needs to be named differently than the external method or else the proxy will call this method recursively // alternatively the target-class in "method.invoke(MyClass.this, args);" could be a different instance of JsCallInterface } public void myMethod1Intern(String param1, String param2) { // do actual work here } } 

这是Oracle HotSpot JVM中的一个装饰性错误 – 在你看到的堆栈跟踪中- locked <0x00007f3e9a0b3830>它实际应该说- waiting to lock <0x00007f3e9a0b3830>

有关详细信息,请参阅此错误 。