[Java] Diagnosis of a JRockit Deadlock

原文はこちら。
https://blogs.oracle.com/poonam/entry/diagnosis_of_a_jrockit_deadlock

最近、興味深いJRockit JVMのデッドロックに出くわしました。私はのための修正まで、役に立つかもしれないデッドロックの詳細、その診断方法、デッドロックの回避方法を共有します。この内容は将来のJRockitのリリースで修正されるまでお役に立つかと思います。

このデッドロックはアプリケーションのJavaスレッドとJVMのコード生成スレッドの間で発生しました。両スレッドの呼び出しのトレースは、以下のようになっていました。
"[ACTIVE] ExecuteThread: '0' for queue:  'weblogic.kernel.Default (self-tuning)'" id=15 idx=0x54 tid=27249 prio=5 alive, native_waiting,daemon
at <unknown>(???.c)@0xffffe410
at eventTimedWaitNoTransitionImpl+79(event.c:90)@0xf7c910a0
at syncWaitForSignalNoTransition+81(synchronization.c:28)@0xf7e0d8a2
at innerNativeDoWait+894(nativelock.c:614)@0xf7d9e8cf
at nativeWait+71(nativelock.c:721)@0xf7d9ec28
at cbrCompileInCodeGenThread+451(compilerqueue.c:339)@0xf7c71b94
at dispatch_compile_request+78(compilerbroker.c:511)@0xf7c64eaf
at cbrGeableCodtRunneInfo+154(compilerbroker.c:580)@0xf7c6516b
at stubsCallJava+77(stubcall.c:112)@0xf7e061ce
at stubsCallJavaV+344(stubcall.c:276)@0xf7e065c9
at javaInvokeStaticVoidMethod+39(javacalls.c:178)@0xf7cd6098
at clsEnsureInitialized+485(class.c:256)@0xf7c4c446
at check_flags_and_clinit+28(compilerbroker.c:302)@0xf7c641fd
at cbrGetRunnableCodeInfo+37(compilerbroker.c:564)@0xf7c650f6
at stubsCallJava+77(stubcall.c:112)@0xf7e061ce
at javaInvokeMethod+280(javacalls.c:1128)@0xf7cd62a9
"(Code Generation Thread 1)" id=4 idx=0x30 tid=27235 prio=5 alive,native_waiting, daemon
at <unknown>(???.c)@0xffffe410
at eventTimedWaitNoTransitionImpl+79(event.c:90)@0xf7c910a0
at syncWaitForSignalNoTransition+81(synchronization.c:28)@0xf7e0d8a2
at innerNativeDoWait+894(nativelock.c:614)@0xf7d9e8cf
at nativeWait+71(nativelock.c:721)@0xf7d9ec28
at clsEnsureInitialized+334(class.c:219)@0xf7c4c3af
at check_flags_and_clinit+28(compilerbroker.c:302)@0xf7c641fd
at compileit+273(compilerbroker.c:317)@0xf7c64992
at cbrCompileRequest+16(compilerbroker.c:537)@0xf7c651b1
at cg_thread+876(compilerqueue.c:223)@0xf7c7168d
at thread_stub+318(lifecycle.c:808)@0xf7d5205f
at start_thread+225(:0)@0x64a832
at __clone+93(:0)@0x5cee0e
上記のスタックトレースとコア·ダンプ·ファイルの検査の結果、Javaスレッド(TID=27249)はクラスの初期化中で、そのクラスの "clinit'メソッドを呼び出す途中であったことがわかりました。メソッド 'clinit'はコンパイルされていなかったので、そのメソッドをコンパイルするdispatch_compile_request() を呼び出し、コード生成スレッドにコンパイル要求を送信していましたが、コード生成スレッドがその要求を受け入れ、完了するために'clinit"のクラスの初期化が完了するのを待っている時にスタックしてしまいました。関数dispatch_compile_request()は、コード生成に利用可能なスタック領域がスレッドに十分ある(>64K)かどうかをチェックし、十分にスタック領域があれば、同じスレッドでメソッドをコンパイルしますし、そうでない場合は、コード生成(コンパイラ)スレッドにコンパイル要求を渡します。
if (!enough_stack) {
...
res = cbrCompileInCodeGenThread(req, TRUE);
} else {
res = compileit(req, req->method);
}
このケースでは、Javaスレッド(TID=27249)で使用可能なスタックの領域は63K前後だったことが問題の原因でした。ここで発生したことをまとめます。
  1. メソッドのコンパイル時に、コード生成スレッドがそのメソッドのクラスの初期化を期待する。初期化されない場合は、クラスが初期化されるのを待つ。
  2. コンパイルするのに十分なスタック領域がないため、クラスの初期化中のJavaスレッドがコード生成スレッドに当該クラスのメソッドをコンパイルするよう指示し、コード生成スレッドがコンパイル終了するのを待つ。
上記のシナリオが原因で、デッドロックが発生します。

このような状況を回避するための簡単な解決法は、アプリケーションのJavaスレッドのスタックサイズを増やして、常に同一スレッドでコンパイルが可能になるよう、十分なスタック領域を確保しておくけば、少なくとも初期化されていないクラスのコンパイルの指示をコード生成スレッドに渡さなくてよくなります。

このバグの修正が現在進行中であり、将来のJRockitのパッチリリースで利用できるようになります。

[訳注]
このデッドロックの症状はJRockit R28.2.4で修正されています。

JRockit hang fix (Poonam Bajaj)
https://blogs.oracle.com/poonam/entry/jrockit_hang_fix

0 件のコメント:

コメントを投稿