本連載は、現場でのエンジニアの経験から得られた、APサーバをベースとしたWebアプリ開発における注意点やノウハウについて解説するハック集である。現在起きているトラブルの解決や、今後の開発の参考として大いに活用していただきたい。(編集部)
Javaを用いたシステムで発生したトラブルを解析する際、スレッドダンプは非常に有効な手掛かりを指し示してくれる。
例えば、連載第3回の「【実録ドキュメント】そのログ本当に必要ですか?」ではログ出力がボトルネックとなったトラブルを、解析ツールを用いたスレッドダンプ解析により発見している。また、連載第10回の「ThreadとHashMapに潜む無限回廊は実に面白い?」では、レースコンディション(競合状態)下においてHashMap内に無限ループが発生したトラブルを、スレッドダンプとpsコマンドの実行結果の突き合わせにより発見している。
今回は、ちょっと変わったスレッドダンプの読み方として、スタックトレースの“字面”に着目したトラブルハックを紹介しよう。ぜひ、その“違和感”を感じとってほしい。
HashMap内に発生した無限ループ(第10回参考)の解析結果報告も兼ねて、3年ぶりに再会した同期A君と飲みに行った。このとき、A君が抱えている別案件で発生している問題の相談を受けた。なんでも、性能試験時に不可思議な出来事が起きているという。簡単に要約すると、次のとおりだ。
スレッドダンプを確認したかと尋ねると、確認してDBコネクション周りに問題があるように見えるが、DBサーバには特段異常がないため、原因が想像できないという。
A君らしくもないと思いつつ、3カ月ほどトラブルのない生活を送り、仕事に余裕が生まれていたことも手伝い、ログを一式送ってもらえれば解析を手伝うよ、とだけ伝えてその話は終わった。
翌日、事象再現時のログ一式を受領し、早速解析を開始した。CPU、メモリ、ディスクI/O、ネットワークI/Oともに異常はなく、GCログも至って普通の状況だった。そこで、トラブル解析フローに従い、スレッドダンプを解析することにした。
スレッドダンプの解析には、解析ツール「Samurai」を利用したが、見事に真っ赤であった。
分かりやすいトラブルでよかったと胸をなで下ろしつつも、早速、赤いスタックトレースを確認すると、以下のようになっていた。
ログ1 スタックトレース1(get) |
"TP-Processor122" prio=6 tid=0x05779c00 nid=0x134c waiting for monitor entry [0x064df000..0x064dfa80] |
一目見て、DBコネクションプールの枯渇によるボトルネック発生を疑った。しかし、「troublehack11.DBConnectionPool」というこれまで見たことがないクラスであったため、DBコネクションプールを作り込んでいる可能性が頭をよぎる。
ロック競合でボトルネックが発生しているスレッドダンプを解析するときのポイントは、ロックを保持しているスレッドを発見し、そのスタックトレースを確認することだ。まずは、ロックされているオブジェクトのIDを調べるため、次の行に着目する。
ログ1 スタックトレース1のwaiting to lockの行 |
- waiting to lock <0x238b5ad8> (a troublehack11.DBConnectionPool)
|
ここで、「<0x238b5ad8>」となっている個所が、ロックされているオブジェクトのIDとなる(※補足:ここでは便宜上「ID」と呼んでいる。この値の意味は、Java VMの実装により異なるため、普段はあまり気にしなくてよい。サン・マイクロシステムズが提供するJava VMの場合、オブジェクトが存在するメモリのアドレスを指し示しているため、GCによってオブジェクトが移動すると、この値は変化する可能性がある。よって、スレッドダンプをまたいでこの値を利用する際には、注意する必要がある)。
次に、このロックを保持しているスレッドを検索しよう。ロックを保持しているスレッドのスタックトレースには、いましがた調べたオブジェクトのIDを伴った次のログが表れているはずだ。
ログ2 ロックを保持していることを表すスタックトレース |
- locked <0x238b5ad8> (a troublehack11.DBConnectionPool) |
Samuraiでは、ログ上のオブジェクトIDの個所をクリックするだけで、ロックを保持しているスレッドのスタックトレースを表示できる。早速スタックトレースを表示すると、次のようなスレッドが得られた。
ログ3 スタックトレース2(wait) |
"TP-Processor98" prio=6 tid=0x01b1cc00 nid=0xa58 in Object.wait() [0x05d5f000..0x05d5fa80] |
「Apache Commons DBCP」パッケージ(以下、DBCP)のコネクション取得個所で止まっている。確かに、これはDBコネクションが不足している際に表示されるスタックトレースであり、A君の解析結果どおりの内容だ。そして、よくよくスタックトレースを呼び出し元の方へ目を移すと……。
その違和感に思わず「えっ」と声を上げてしまった。さて、何に違和感を覚えたか、考えてみてほしい。
Copyright © ITmedia, Inc. All Rights Reserved.