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)
...
つまりスタックトレースに「locking to lock ...」行がありません。どうやらスレッドはClassオブジェクトをロックしていますが、スレッド自体がブロックされている理由はわかりません。
スレッドダンプには、デッドロックのヒントは含まれていません。
ロックモニターを識別するにはどうすればよいですか?
ありがとう、オリバー
明らかに、これらの種類のブロックされたスレッドが観察された状況は、大量のメモリ消費とそれに伴う大量のガベージコレクションに関連していました。
この質問 Javaブロッキングの問題:JVMが多くの異なるクラス/メソッドでスレッドをブロックするのはなぜですか? は同様の状況を説明しているため、これらのスレッドはガベージコレクターによって単にブロックされたと思います。
(とにかく、メモリの問題を解決した後、ブロッキングスレッドに関するこの問題はなくなりました。)
ファイナライザスレッドがブロックされているか、待機しているかどうかを確認します。
GCスイープ中に、GCはクリーンアップを実行するために「世界を停止」します。 「世界」の定義は、使用されているガベージコレクターとコンテキストによって異なります。スレッドの小さなクラスタまたはそれらすべての場合があります。ガベージを正式に収集する前に、GCはオブジェクトのfinalize()を呼び出します。
ファイナライザメソッドを実装している望ましくない状況にある場合、ファイナライゼーションコードがコードの終了をブロックしている可能性があり、「世界」が停止したままになります。
これは、多くのスレッドが未知の魔法の力によって永続的にブロックされているのを見ると最も明白です。ブロックが発生しているコードを検索すると、意味がありません。近くにブロックコードはありません。ダンプがないため、待機しているモニターを漏らしません。 GCがスレッドを一時停止しました。
Google Chromeでアプレットを使用したところ、同様の問題が発生しました。
要するに:
詳細:
次のシナリオがありました。
Executor
とnew Runnable() ...
を使用して呼び出しを切り離し、待機時間を短縮してJSでハングします。説明:
new Runnable()
は、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呼び出しによってブロックされており、それ自体は見つからないクラスによって読み込まれます。
ソリューション:
#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>
と表示されます。
詳細は このバグ を参照してください。