23

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

我看到自己被阻塞的阻塞线程(导致其他线程等待),但是线程转储并没有告诉我他们等待的原因或等待哪个监视器。

例子:

"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 <0x00007f3e9a0b3830> (a java.lang.Class for org.catapultframework.resource.ResourceObject)
        at java.lang.Class.getAnnotation(Class.java:3029)
        ...

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

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

如何识别锁定监视器?

谢谢,奥利弗

4

4 回答 4

11

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

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

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

于 2011-08-26T13:07:41.880 回答
8

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

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

如果您在实现终结器方法的不良情况下,终结代码可能会阻止它完成并且“世界”保持停止。

当看到大量线程被某种未知的魔法力量永久阻塞时,这一点最为明显:查找发生阻塞的代码,这将毫无意义;在它附近的任何地方都找不到阻塞代码,并且转储不会泄露它正在等待的监视器,因为没有监视器。GC 已暂停线程。

于 2015-02-15T02:53:38.863 回答
3

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

简而言之:

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

详细地:

我有以下情况:

  1. 我在 Chrome 中使用带有代码库 = 文件夹到单个类文件(无 jar)的 Applet
  2. 网站使用 LiveConnect 将焦点事件传递给小程序
  3. 传入的 JS 调用使用Executorwithnew Runnable() ...来分离调用以减少等待时间,从而在 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 调用在这里被 BLOCKED:

"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 进行任何调用之前,在小程序的构造函数中触发加载所有相关类。
  2. 如果类文件不是单独加载,而是从 jar 文件加载,它可能会有所帮助。这背后的理论是:类加载器不需要与浏览器对话来从 jar 文件中加载类(这将是
  3. 结合 1.:使用动态Proxy类来包装所有传入的 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
    }
}
于 2014-03-06T17:12:51.863 回答
2

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

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

于 2014-04-07T10:18:10.377 回答