nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JBossがおかしい!あなたならどうしますか?

Java Advent Calendar 2011JBoss Advent Calendar 2011の28日目のエントリです。タイトルはMySQLがおかしい!あなたならどうしますか? – MySQL Casual Advent Calendar 2011のパクリです。素晴らしいエントリですね。

このエントリでは対象をJBossJava VMに置き換えてお送りします。

JBossがおかしい!

(略)

まずはserver.log

このファイルがトラブルシューティングの基本になります。WARNかERRORレベルで出力されているものをチェックすれば大抵何が起きているのかはわかります。ほとんどの場合はエラーメッセージや例外メッセージでぐぐるとそれなりに情報は出てくるでしょう。

$ grep -nEe "WARN|ERROR" server.log | less
$ grep -nEe "WARN|ERROR|Caused by" -e "^[._[:alnum:]]+(Exception|Error)" server.log | less

例外も引っ掛けたい場合は下のほう使って下さい。

server.logは以下の位置にあります。同じディレクトリにあるアクセスログとboot.logも役に立つのでトラブルのときは一緒に取得しておきましょう。

  • JBoss AS 5.x or 6.x
    • $JBOSS_HOME/server/$PROFILE/log/server.log
  • JBoss AS 7.x
    • $JBOSS_HOME/standalone/log/server.log

OutOfMemoryErrorが発生してる

Java VMの基本情報とヒープダンプを取得して解析。

スレッドがハングしてる、もしくは暴走してCPU使ってる

スレッドダンプとGCログを取得して解析。

ログ見ても原因がわからない

DEBUGログ有効にしても原因がわかるようなログが出ない、そんな場合はBytemanを使いましょう。

ソースコード非公開のクソ製品のデバッグでもjavapしてメソッドシグニチャ引っこ抜いてルールが書けるので使えますよ!

Java VMがクラッシュした

どうせ古いJava VM使ってるんだろ?最新版使ってるとほとんどクラッシュしないよ?

というのはさておき、OpenJDKであれば自力で解析できるっちゃーできるんですが、解析のために必要な知識がかなり膨大なのであまり現実的な選択肢ではないと思います。クラッシュの調査とか対策とか修正を期待する場合はOracleJava for BusinessとかRHELに含まれるOpenJDKなどのサポートつきのJava VM使って下さい。その場合はコアダンプ取るのにulimit -c unlimitedで制限解除しとくのを忘れずに。

Java VMの情報取得

Java VMの情報を取得するにはJConsoleとかもありますが、あれはGUIツールであり保存が効かない。生データを完全な形で保存するようにしましょう。

Java VMについてくるツール群を利用してまずはプロセスIDを調べ、各種ツールで情報を収集します。

プロセスIDを調べる
$ jps -mlv

mlvオプションを付けたことない人は一度付けて実行してみるよろし。

基本的なVMの情報を取得
$ jinfo <pid>
ヒープサマリ取得
$ jmap -heap <pid>
ヒストグラム取得
$ jmap -histo:live <pid>
ヒープダンプ取得
$ jmap -dump:live,format=b,file=heap.bin <pid>

ヒープダンプはメモリ内容を全部ディスクに書き出す、というようなことをするので、取得にちょっと負荷と時間がかかるので注意。

スレッドダンプを取得
$ jstack <pid>
ランタイムでJMXからGCログを有効化

良い子はちゃんとJava VMオプションで事前に有効化しておきましょう。ランタイムからだと-Xloggc:相当のファイルに出力させる設定とかできません。

  • "com.sun.management:type=HotSpotDiagnostic" MBean
    • setVMOption(String, String) operation

PrintGCDetails, trueとかセットする。-verbose:gcオプションに相当するものは"java.lang:type=Memory" MBeanにVerbose attributeとかあるけどこのオプションは実は必要ないんだよね。

とりあえず全部とっとくシェルスクリプト

ぶっちゃけこういうの一個作っておけば上の覚えておかなくてもいいんじゃねーの。

#!/bin/sh

PID=$1

if [ -z $PID ]; then
  echo "Usage: $0 <pid>"
  exit 1
fi

DATE=`date +%Y%m%d%H%M%S`

DUMPFILE=dumpj-$DATE.txt

{
    date

    echo "============ jinfo $PID ============"
    jinfo $PID

    echo "============ jmap -heap $PID ============"
    jmap -heap $PID

    echo "============ jmap -histo:live $PID ============"
    jmap -histo:live $PID

    echo "============ jmap -dump:live,format=b,file=heap-$DATE.bin $PID ============"
    jmap -dump:live,format=b,file=heap-$DATE.bin $PID

    # Take thread dump every 5 sec for 3 min
    MAX_COUNT=36
    INTERVAL=5
    COUNT=0
    while [ $COUNT -lt $MAX_COUNT ]
    do
        COUNT=`expr $COUNT + 1`
        echo "============ jstack $PID loop $COUNT ============"
        jstack $PID
        echo "============ top -b -n 1 -H -p $PID =========="
        top -b -n 1 -H -p $PID
        sleep $INTERVAL
    done
} > $DUMPFILE

おわりに

というわけで、grepの仕方やJava VMの基本的な情報収集のパターンをきちんと抑えておくことでトラブル時の情報収集の速さ、情報量は格段に違うし解決速度も段違いに変わるんだよ、というお話でした。