23

Tomcat 6 (java 1.6.0_22、Linux) で実行されている Spring MVC Web アプリケーションの jstack から取得したスレッド ダンプを理解するのに苦労しています。

ブロックされているスレッド (他のスレッドを待機させる) が表示されますが、スレッド ダンプでは、スレッドが待機している理由や待機しているモニターがわかりません。

例:

"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)
        ...

つまり、スタック トレースに "waiting to lock ..." 行がありません。どうやらスレッドが 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 でアプレットを使用して同様の問題が発生しました。

要するに:

  • VM がクラスをロードする必要がある場合、BLOCKED スレッドをブロックできます。
  • クラス自体をロードするプロセスが何かによってブロックされると、アプリ全体のフリーズが発生する可能性があります。

詳細に:

次のシナリオがありました。

  1. コードベース=フォルダーから単一のクラスファイル(jarなし)でChromeでアプレットを使用しています
  2. Web サイトは、LiveConnect を使用してフォーカス イベントをアプレットに渡します。
  3. 着信 JS 呼び出しはExecutorwithを使用しnew 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 呼び出しがここでブロックされています。

"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 に答える