NetBeansプロファイラでは、アプリケーションのメモリ使用量つまり、ヒープサイズの使用量をグラフィカルに監視・測定することができます。NetBeansに組み込まれているので、別途プロファイラを用意するのと比べてとても簡単に測定することができます。
メモリリークするサンプル
メモリリークするサンプルは簡単に作るのはなかなか難しいので、単純にメモリを消費するだけの無駄アプリを作って調査します。
画面デザインはこんな感じです。
↓
「メモリリークするサンプル」ボタンはトグルボタンになっていて、押下している間は無駄にオブジェクトを生成していきます。
「クリア」ボタンを押すと、生成されたオブジェクトが開放されるはずです。
ソースコードは、こんな感じになります。
「メモリリークするサンプル」ボタンの押下時の処理です。
private List list = new ArrayList();
private void jToggleButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Runnable run = new Runnable() {
public void run() {
long l = 0;
while (jToggleButton1.isSelected()){
try {
//無意味にバイト配列をListに追加
list.add(new byte[10000]);
Thread.sleep(10);
jLabel1.setText(String.valueOf(l));
l++;
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
};
new Thread(run).start();
}
押下時に「メモリを無駄に消費するスレッド」が起動し、ボタンが戻るまでオブジェクトが生成され続けるようになっています。
クリアボタンのコードはこんな感じです。
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
//Listをクリアする
list.clear();
System.gc(); //GCを実行
}
プロファイリング
プロファイリングタスクは、「メモリー使用量を解析」を選択します。
「オブジェクト生成とガベージコレクションの両方を記録」を選択してください。
実行すると(おそらく左側に)「Profilerウィンドウ」が表示されるはずです。
このウィンドウのなかからこのボタンを押すと、下部に「VM遠隔測定結果の概要」というグラフが3つ表示されます。一番左のグラフに注目します。
下部に表示されたグラフはダブルクリックすると拡大して表示されます。
このグラフはリアルタイムに変化します。「メモリリークするサンプル」ボタンを押したり、「クリア」ボタンを押したりしてメモリ使用量の変化を観察することができます。
メモリ使用量のプロファイリングは見ていてなかなか面白いです。
使用済みヒープサイズが高い時(紫色の領域が高いとき)に、「スナップショットを作成」ボタンを押すと、現在のヒープ領域に存在するオブジェクトが記録されます。
この結果を見ると、確かにbyte[]が大きな領域を占有しているのが分かります。
スナップショットは、アプリケーションの終了時にも自動的に記録されます。
アプリケーションのメモリ消費が大きくて、メモリリークの疑いがある場合には、プロファイラを使用することで詳細に調査することができます。どのように消費されていくのか?何のオブジェクトが大量に使われているのか?がすぐに分かります。
プロファイルデータ自体がメモリーを消費して、byte[]などが増えます。有効活用が難しいのですが、何かコツってあるのでしょうか?
特にWEBアプリだと、ServletからJSPにArrayListでオブジェクトを渡していたりするのですが、WEBアプリのプロファイルって結構難しくて、うまくメモリが解放されていない気がします。
JSP表示後に、明示的にClear()は必要なのでしょうかね。
>JSP表示後に、明示的にClear()は必要なのでしょうかね。
通常は必要ないと思います。
例えば、Servletで生成したArrayListをJSPに渡して表示した場合は、どのような方法で渡すかで生存期間が違うと思います。セッションを使って渡すとしばらくは開放されないです。
また、GCされるまではメモリは開放されないので、短期的に開放されないのは気にしなくてよく、右肩上がりに常に増加し続けるようなケースがメモリリークです。JSP表示直後に開放されるわけではないです。のこぎり上にギザギザになるのは正常です。