JmxHeapWatchDogで出来ること

前回の続き。

ka-ka-xyz/JmxHeapWatchDog · GitHub

JmxHeapWatchDogでどんな設定を行えば良いのか、どんなことが出来るのか、サンプル的にまとめ。サンプルとして、JmxHeapWatchDogのJavaVM自体を監視してみる。

1. JmxHeapWatchDogのインストール

GitHubからソースを落としてきてローカル環境でビルドするか、ここからバイナリを落としてください。

インストール方法については下記Readme
JmxHeapWatchDog/README-ja.md at master · ka-ka-xyz/JmxHeapWatchDog · GitHub
を参照。

2. 監視対象JavaVMのJMXを有効化

JmxHeapWatchDogはWindowsサービスとして起動するためにcommons-daemonを使用しています。
commons-daemonJava起動オプションはレジストリ中に下記値として保存されています。

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apache Software Foundation\Procrun 2.0\JmxHeapWatchDog\Parameters\Java\Options

Jmxを有効化するため、このOptionsに以下の値を追加後、再起動します。

  • Dcom.sun.management.jmxremote.port=17999
  • Dcom.sun.management.jmxremote.authenticate=false
  • Dcom.sun.management.jmxremote.ssl=false

ここではJmxが使用するポート番号として17999を指定しています。ポート番号がかぶる場合には別の番号に置き換えてください。

3. JmxHeapWatchDogの設定

JmxHeapWatchDogはデフォルトでlocalhostのポート17999を監視するようにしています。
ポート番号を変えた場合、confフォルダのjvms.xmlを開き、以下のように編集します。

・編集前

<?xml version="1.0" encoding="UTF-8"?>
<JavaVMs interval="5">
  <jvmInfo name="javaVm1" host="localhost" port="17999"
    csv_path="C:\Java\jmxlogs" ></jvmInfo>
  <!-- 
  <jvmInfo name="javaVm2" host="localhost" port="27999"
    csv_path="C:\Java\jmxlogs2" ></jvmInfo>
   -->
</JavaVMs>

・編集後(ポート番号を18000へ変更した場合)

<?xml version="1.0" encoding="UTF-8"?>
<JavaVMs interval="5">
  <jvmInfo name="javaVm1" host="localhost" port="18000"
    csv_path="C:\Java\jmxlogs" ></jvmInfo>
  <!-- 
  <jvmInfo name="javaVm2" host="localhost" port="27999"
    csv_path="C:\Java\jmxlogs2" ></jvmInfo>
   -->
</JavaVMs>

jvms.xmlの詳細についてはReadmeを参照。

4.JmxHeapWatchDogサービスの起動

「コンピュータの管理」からサービス一覧を出してJmxHeapWatchDogを起動します。上手く起動しない場合、Readmeの「インストール」に書いてる注意書きに従って、jvm.dllの場所を明示してください。

5. 放置

取り敢えず30分ほど放置します。"C:\Java\jmxlogs"フォルダに、"javaVm1_yyyy-MM-dd.csv"というファイルが作成され、メモリ使用状況が書き込まれるはずです。

6.解析

JmxHeapWatchDogサービスを停止し、csvデータの解析を行います。
csvの一行目にヘッダが書かれていますが、各ヘッダの意味についてはReadmeを参照してください。

とりあえずExcelなりOpenOffice Calcなりで開いて適当にグラフ化。

まずは以下のヘッダについてグラフ化してみました。

  • Total Heap Space used: 使用中のヒープ領域全体サイズ
  • Total NonHeap Space used: 使用中の非ヒープ領域全体サイズ
  • PS MarkSweep count: Old世代へのGC(Full GC)実行回数
  • PS Scavenge count: 通常GC実行回数

f:id:ka-ka_xyz:20140330175705p:plain

ヒープ領域はGC実行毎にきっちり減少し、かつ時間が経つごとにヒープ領域の使用量が増加していくような危険な兆候は見られませんでした。
また、起動時にFull GCが走っているようですが、その後特にFull GCが実行されていません。

では次に、ヒープ領域の中身について見てみるため、以下のヘッダについてグラフ化してみます。

  • PS Survivor Space used: 使用中のSurvivor領域サイズ
  • PS Old Gen used: 使用中のOld領域サイズ
  • PS Eden Space used: 使用中のEden領域サイズ

新規に作られたインスタンスはまずEden領域に置かれ、ある程度寿命が長いものはさらにSurvivor領域へ送られ、さらに寿命が長いオブジェクトはOld領域へと送られると。

f:id:ka-ka_xyz:20140330180517p:plain

右のY軸はEden領域の使用量、左のY軸はSurvivor/Old領域の使用量を示します。Eden領域の使用量ははSurvivor/Old領域の使用量と比べると1桁多いことが分かります。つまり、大多数のオブジェクトは作られた後で後腐れなくGCされていると思われます。
また、のタイミングで、Survivor領域の使用量が減少し、Old領域の使用量が増加していることが見て取れます。世代別管理がうたい文句通りに上手く機能していることが判明(いや当たり前ではあるんですが)。ただまあ、もうちょっと長時間データを取ってみないと何とも言えませんが、Old領域の使用量がこの調子で増えていくとちょっと問題になりそうな。

インスタンスの世代管理とGCとの関係については以下のURL等を参照。

第6回 HotSpot JVMのヒープ構造の仕組みを把握する:Javaはどのように動くのか~図解でわかるJVMの仕組み|gihyo.jp … 技術評論社
Java技術最前線 - 「メモリーを意識してみよう」第2回 GCの仕組みを理解する:ITpro
現場から学ぶWebアプリ開発のトラブルハック(2):“Stop the World”を防ぐコンカレントGCとは? (1/2) - @IT

正直、自分も「だいたい」以上には理解しきれてません。