はじめに
VisualVMはJavaのメモリ使用率とかを確認できるツール。連続負荷試験とかを実施するときにメモリリークの傾向とかがないかどうか等を確認するために使ったりしてる。
別に新しくもないツールだとは思うがまだ現役のツールだと思っているし、あんまり知らない人もいるようなので、メモリリークの傾向のあるサンプルプログラムを使い、簡単な使い方を(知っている範囲で)記述する。
サンプルプログラム
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MemoryLeak {
private static class SampleClassA {
private final List<byte[]> list = new ArrayList<>();
private void malloc() {
list.add(new byte[1024]);
}
private void clear() {
list.clear();
}
}
private static class SampleClassB {
private final List<byte[]> list = new ArrayList<>();
private void malloc() {
list.add(new byte[1024]);
}
private void clear() {
;
}
}
public static void main(String[] args) throws Exception {
SampleClassA sampleA = new SampleClassA();
SampleClassB sampleB = new SampleClassB();
while(true) {
sampleA.malloc();
sampleB.malloc();
sampleA.clear();
sampleB.clear();
TimeUnit.MILLISECONDS.sleep(100);
}
}
}
SampleClassAとSampleClassBでそれぞれ100ミリ秒に1kBだけメモリを確保→開放している。見た目はほとんど同じだが、SampleClassBのほうは実際にはメモリを開放していない。
使い方
上記サンプルプログラムを使って簡単にテストしてみる。
1. VisualVMを起動しておく
起動する。今回の例ではEclipseのPleiadesにあるVisualVMを使う(Windowsならjava/8/bin/jvisualvm.exeとかで起動できるはず)。
2. テストするプログラムを実行する
今回は上記サンプルプログラムをEclipseから実行する。
実行すると下のような感じで実行中のプログラムがVisualVMに出てくる(Eclipseとは別でVisualVMをダウンロードしてたりすると出てこないかもしれない)。
これをクリックすると、下のように現在のCPU使用率やメモリ使用率が出てくる。
3. テスト開始時の状態を記録する
後からテスト開始時の状態と比較するため記録しておく。とりあえず監視タブからGCを実行した後スクリーンショットでもとって、現在のメモリ使用量でも記録しておく(2の画像の青の部分が現在のメモリ使用量)。また、監視タブの右上からヒープダンプボタンを押すと、以下のように現在読み込み中のクラス情報とかが得られる。
4. しばらく放置する
一時的ににメモリ使用量が増加しているように見えても途中で増加が止まることもあるようなので、それなりの期間放置する。昔いた会社では1週間程度、最低でも3日は放置して監視していたが、実際どの程度の期間を監視期間とすべきなのかは場合による。
5. テスト後の結果と比較する
面倒なので2時間程度で終了とし、テスト前の結果と比較する。
GCを実行した後のメモリ使用量は80MBくらい。テスト前は10MBくらいだったので上昇しているのがわかる。
ヒープダンプを新しく取得することで、前回のヒープダンプとの比較もできる。前回と同じようにヒープダンプを取得し、クラスタブの右上から別のヒープダンプとの比較を選択すると、以下のような感じでどういったインスタンスが増加しているのかがわかる。今回のケースだとbyte[]が増加している。
また、ここからさらにそのインスタンスがどこから参照されているのかも調べることができる。クラスタブのbyte[]をダブルクリックし、左側のインスタンスから適当なものをクリックした後、右下の参照の中を開いていくと、byte[]インスタンスのうち少なくとも一つがSampleClassBから参照されていることがわかる(型の部分を参照)。
本番では様々なインスタンスが様々な所から参照されているが、今回の場合は他のインスタンスの参照を開いても大体はSampleClassBから参照されていることが把握できる。
おわりに
- 今回はメモリに関することを記述したが、実際にはほかにも色々調査できるはず
- ファイルのクローズし忘れによるファイルディスクリプタの増加等、このツールだけでは把握できないこともある(と思っている)ので、別途ほかのツールやコマンド等を使ってのテストも必要