第3回になります。
記事を書くこと自体に時間がかかってしまうので
見やすい記事にするってのが難しいですね。。
少しずつ改良していきます。
ストックやフォローをして頂けると励みになります。
さて、本題です。
jstatやGCログなどを見てメモリがなかなか開放されない場合は
ヒープダンプを取ってメモリの中身を解析することになります。
ヒープダンプの取得
まず、jstatやpsコマンド等で対象のJVMのプロセスIDを取得しましょう。
jps -v | less -SN
プロセスIDがわかったらjmapコマンドでダンプを取ります。
サーバのディスク容量に注意してください。
heapサイズを2GBとかに設定した場合は2GB以上のファイルが
生成されることがあります。
jmap -dump:format=b,file=./heapdump.hprof {プロセスID}
これで指定した場所にダンプファイルができます。
-dump:live,format=...というようにliveをつけるとGCが起きて
コマンド実行時に活きているオブジェクトのみのダンプになるようです。
とりあえず今回はフルで取ります。
ヒープダンプの中身を解析をする
解析をするにはツールを使います。
java標準のjhatやMemory Analyzer、HeapAnalyzerなど
いくつかありますが今回はMemory Analyzerを使います。
Eclipseベースなのでサイズの大きいダンプファイルを開く場合は
MemoryAnalyzer.iniの設定でツール自体のヒープサイズを
増やすようにしてください。
file->Open Heap Dumpより先ほど取得したファイルを指定して、
次に出てきたダイアログでLeak Suspects Reportを選択。
少し待つとoverviewが開くのでReportのLeak Suspectsをさらに選択しましょう。
そうするとツールがメモリリークの疑いがある場所を探してくれます。
※あくまで疑いなので、違う場合もあるし見つからないリークもある。
上記の図ではT4CPreparedStatementが5000弱のインスタンスを保持していて
25%の容量を使っていると教えてくれています。
Details >> というリンクを押すと対象オブジェクトを持つ上位参照を教えてくれます。
どうやら50個のTxConnectionListenerの配下に約100個ずつT4CPreparedStatementを保持していて
5000個弱(4953個)のT4CPreparedStatementを持っている様子。
TxConnectionListenerが直接T4CPreparedStatementを持っているわけではないようで
このビューではここまでしか見れないようなので
Dominator TreeからT4CPreparedStatementを検索して参照元を深堀しましょう。
中身はSQL情報のキャッシュなどでした。まぁ、PrepareStatementですからね。
ではこれの参照元を辿りましょう。
T4CPreparedStatementをクリックしてPath To GC Roots->with all referencesを選択すると
参照元を追っていけます。
階層を見ていくとcache(PreparedStatementCache)が保持しているということがわかります。
ここまでの情報をからコネクション(TxConnectionListener)が50個あってそれらが
キャッシュ(PreparedStatementCache)を100個持っているということがわかったので
DB(データソース)の接続設定がこの通りなのかサーバ設定を確認しましょう。
ConnectionPool Sizeが50、 Statement Cache Sizeが100でした。
仮説は当たっているようですね。どう見ても多すぎです。はい。
適切値に直してヒープダンプを取り直したところメモリの使用率も大幅に削減できました。
今回はミドルウェア設定が起因でしたが
開発したアプリケーション起因のリークが疑わしい場合は
サーバ起動時、数分後、数時間後などDumpを何度も取って確認しましょう。
2つのヒープダンプの差分を取る機能もあるので残り続けている(リークしている)オブジェクトはないか・・・
異様に大きいサイズのオブジェクトはないか・・・などをチェックしていきます。
今日はここまで!