サーバ

メモリリークトラブルシューティング記 – その4: リーク箇所を確認する色々な方法

– その1: 自宅サーバがハング

– その2: フリーズの原因はガベージコレクション

– その3: 侍でヒープ使用量を確認

– その4: リーク箇所を確認する色々な方法

前回のエントリではOld 領域にオブジェクトが溢れていることを突きとめた経緯を書きました。

次は、ヒープ領域にどんなオブジェクトが溜まっているのか確認する必要があります。
リークしているオブジェクトを検出する方法はいくつかあり、それぞれ以下のような特徴があります。

1. プロファイラ
リーク検出を行う上で一番有名な方法。
JVM に特殊なフックをかけ、オブジェクトの生成、GC 等の挙動を監視します。
アプリケーションを動作させながら増加してくオブジェクトだけを抽出したり、オブジェクト生成に至るスタックトレースを確認したりと様々な視点で VM を調査できるのが特徴です。
一般的にプロファイラを使うと VM のパフォーマンスが劣化するため運用環境では利用できません。
再現方法が分かっている場合にプロファイラをアタッチさせて VM を起動させ、再現テストケースを数回だけ回して増分オブジェクトを解析するのが一般的です。
ただし、JRockit Runtime Analyzer だけは例外でパフォーマンスの劣化が少ないため運用環境でも利用できます(※)。
これは元々 VM が自己チューニングに利用するために収集している統計情報を見せているだけで、プロファイリングを行う上で追加で情報収拾を行う必要がないからです。

プロファイリングを行える製品としては、NetBeans ProfilerJProbeJRockit Runtime AnalyzerYourKit Profiler などがあります。

それぞれのプロファイラの個人的な印象は以下の通り。
ちなみに YouKit Profiler はまだ試したことがありません。

製品名無償版の有無運用環境の解析オブジェクト生成
スタックトレースの取得
特徴
NetBeans Profiler無償でフル機能のプロファイラ。
まだ登場して間もなく、若干安定性に欠ける印象。
JRockit Runtime Analyzer✔(※1)パフォーマンスほぼ無劣化。
無償でVM起動後1時間まで使えるのでリークの再現方法が分かっていれば結構使える。
YourKit Profiler✔(※2)まだ使ったことないけれども良いらしい。
比較的安価。
JProbe抜群の実績。
ちょっと古めかしいインターフェース。
OptimizeIt Profiler以前はプロファイラといえば OptimizeIt か JProbe と言うくらいだったが今は・・?
しばらく音沙汰がない。
Embarcadero TechnologiesCode Gearを買い取ってからなおさら音沙汰がない。

※1 運用環境の解析は有償(VM起動後1時間までは無償利用可能)
※2 オープンソース開発者には無償ライセンス提供

2. クラスヒストグラム
-XX:+PrintClassHistogram オプションをつけて kill -3 [pid] か、jmap コマンドで取得できるヒープのサマリ情報。
ヒープ中に存在する各クラスのインスタンス数、ヒープ中を占めるバイト数などの情報を取得できる。
取得負荷は低く、運用環境でも手軽に取得できるのが特徴。
オブジェクトグラフ(インスタンス間の参照関係)はわからないので詳細な解析は行えない。
一般に解析するツールが出回っていないため解析がやや面倒。

とある会社内にはクラスヒストグラムを解析するすばらしいツールがあるのを最近見かけたのですが、今のところ公開されていないようです。
Java 界の幸せのためにも是非販売、またはオープンソース化して欲しいところです・・・

3. ヒープダンプ(heapdump)
jmap コマンドで取得できるヒープの詳細情報。
ヒープ中の全てのオブジェクトのインスタンスの数、各インスタンスの参照関係、ヒープ中を占めるバイト数などの情報を取得できる。
JVM を落とさず取得できるが、ヒープ内を全てダンプするため取得負荷が高い。
ヒープ 512mb あたりざっくり5〜10分くらいかかる。

今回は現象の再現方法がわからず、運用環境を解析したいためプロファイラを使う方法は候補からはずれました。
また、クラスヒストグラムは軽く使えて運用環境の解析には向いているのですが、再現周期が不定で、次回発生したときには確実に問題箇所をつきとめたいためこれも候補からはずしました。
というわけで、ヒープダンプで解析することに。

ヒープダンプを取得する方法は簡単。
コマンドラインから

$ sudo jmap -heap:format=b [pid]

と打つとカレントディレクトリに heap.bin という巨大なファイルが生成されます。
増加したオブジェクトを突きとめるためには差分を確認する必要があるので、一般的にはアプリケーションを起動してウォームアップをした後と、リークが発生した後の2回(またはそれ以上)取得する必要があります。

ヒープダンプのファイル名は(たぶん) heap.bin という名前で固定なので上書きしないよう気をつける必要があります。
今回は本番環境のサーバを起動して1時間後、それからフリーズ現象が発生したときの2回取得しました。
繰り返しになりますがヒープダンプの取得は結構時間を要するので気をつけてください。
今回はミッションクリティカルでない個人の自宅サーバの解析です。
なので待機系も用意せず SPOF な JBoss インスタンスから問答無用でダンプを取得しました。
そんなテキトーな運用なので「たまに止まってますよね?」とか時々言われるのですが・・^^;
一般的に運用環境でダンプを取得する際はロードバランサや mod_jk による転送を停止し、別系統でサービスを続行できるようにしておく必要があります。

– その5: Memory Analyzer でヒープダンプを解析(最終回)