JAVAのバグについて
前回の投稿からずいぶんと間が開いてしまいました。桔梗です。
今回は珍しくLinux上でとあるソフトを動かしたときのお話です。
みんなも知っているSendmailというMTAがありますね。これの商用製品のアドオンという形で公開されている製品があるのですが、
この導入作業で遭遇したトラブルのお話です。
まあ、真犯人はバグだらけのJDK1.7系列ということなのですが、なかなか見つける手順というものは探してもないものですよね。
さっくりとお話をしますと、CoreDUMPが原因でフェイルオーバーするシステムがあると。原因はなんでしょうか、とう調査依頼です。
それならgdbで出力されたCoreをみてあげればいいわけです。コマンドライン上からは「gdb Coreを出力した実行ファイル Coreファイル」です。
その後に「thread apply all bt」と打つことですべてのスレッドをバックトレースすることができます。
これで、問題を起こしたスレッド、言い換えればCoreファイルを出力したスレッドの動きを追うことができます。
このときはメモリ周りのエラーでしたので重点的にGlibcやらLinux Kernel側を追っていました。今だからいえますが、これは思いっきり見当違いだったわけです。
真犯人は一番最初のスレッドにありました。ContinueInNewThreadだと?この後のスレッドはほとんどがsem_waitやら、pthread_cond_waitやら続いていました。
そこで、JDKのソースを探してみたところ・・・・・・
OpenJDK / jdk7u / jdk7u / hotspot/src/os/posix/launcher/java_md.cの1888行目以降を見てください。
ContinueInNewThread(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
int rslt;
#if defined(__linux__) || defined(_ALLBSD_SOURCE)
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (stack_size > 0) {
pthread_attr_setstacksize(&attr, stack_size);
}
if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
void * tmp;
pthread_join(tid, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
/*
* Continue execution in current thread if for some reason (e.g. out of
* memory/LWP) a new thread can’t be created. This will likely fail
* later in continuation as JNI_CreateJavaVM needs to create quite a
* few new threads, anyway, just give it a try..
*/
rslt = continuation(args);
}
pthread_attr_destroy(&attr);
とありますね。pthread_join()のところで場合によってはデッドロックが発生しますよね、これ。
たしかこの問題はJAVA1.8(8)で解決されていたように思いますがいかがでしょうか。
詳しい解説はこれから。
1888行目からの処理ですが、
pthread_createで pthreadを起動して、pthread_join でpthreadの終了待ちをしています、
この pthread_join は、ブロッキング関数の為、pthread_create で起動した処理が終了しないと、
デッドロックにはまって、ContinueInNewThread が終了せず、pthreadのIDが、スタックの変数の為、メソッドの外から終了させることもできません。
このソースでは、ContinueInNewThread で作ったソースがタイムアウトで終了する事もなく、外から殺す事も出来ない為、引き渡した処理によってはデッドロックが発生する致命的なバグです。
私も私でよくこんなもん見つけたよね・・・・・・
もちろん、openSUSEにはopenJDKが導入されています。できるだけ、openJDK8を利用した方がいいと思います。