ページの先頭行へ戻る
Interstage Application Server/Interstage Web Server トラブルシューティング集
Interstage

A.6.5 メモリリークを分析する

  ここでは、メモリリークの分析方法を説明します。
まず、リークしているクラス(オブジェクトの種類)を特定します。リークしているクラスが分かったら、ヒープダンプを採取して、オブジェクトの参照関係を調べ、リークの原因となっているオブジェクトを特定します。

A.6.5.1 リークしているクラスの特定

  リークしているクラスを特定する方法について説明します。
まず、Java監視機能の[ヒープ分析]タブを開いて[開始]ボタンを押し、分析を始めてください。

18 Java監視機能による、メモリリーク調査(分析の開始)


  分析が開始されると、次のようなメッセージが表示されます。

19 Java監視機能による、メモリリーク調査(分析開始後)


  [終了]ボタンを押すと、分析が終了します。開始から終了までの時間は、十分とってください。終了のタイミングを計るには、[メモリ]タブを開いて、Javaヒープ使用量の推移を確認します。

20 Java監視機能による、メモリリーク調査(Javaヒープ使用量の確認)


  この例では、Javaヒープの使用量が、上昇し続けていることが分かります。
Javaヒープの使用量が増加し続けることが確認できたら、[ヒープ分析]の画面で[終了]ボタンを押します。

21 Java監視機能による、メモリリーク調査(分析の終了方法)


  分析が終了すると、オブジェクトヒストグラムが表示されます。測定期間中の増加量(バイト数)が多い順に、クラスが表示されます。

22 Java監視機能による、メモリリーク調査(分析結果の表示)


  オブジェクトヒストグラムを見て、大量に増加しているクラスを特定します。

開始時刻   Mon Sep 27 16:40:13 JST 2010
終了時刻   Mon Sep 27 16:44:44 JST 2010

増減[バイト]     増減[個数]       クラス名
142348960        136874           byte[]
12930528         13656            int[]
2189984          136874           com.fujitsu.demo.MemLeak$Blob
558968           0                java.lang.Object[]
105544           83               char[]
33064            135              <methodDataKlass>
2040             85               java.lang.String

23 Java監視機能による、メモリリーク調査(分析結果の抜粋)


  まず、アプリケーションで作成したクラスが増加しているかどうかを確認します。この例では、com.fujitsu.demo.MemLeak$Blobクラスのインスタンスが、13万個以上増加していることが分かります。つまり、このクラスのインスタンスが、メモリリークしている可能性が考えられます。


  この例では、byteやintの配列も、大量に増加しています。しかし、プリミティブ型の配列は、リークとは関係なく増加している可能性も考えられます。
そのため、まずはアプリケーションで作成したクラスの増加量に着目するのが、リーク調査を効率的に行うコツです。アプリケーションで作成したクラスの増加量が小さい場合は、プリミティブ型の配列や、コアライブラリのクラスの増加量に着目します。

  ここまでの調査で、com.fujitsu.demo.MemLeak$Blobクラスのインスタンスがリークしている可能性が大きいことが分かりました。
次に、ヒープダンプを採取してオブジェクトの参照関係を調べ、リークの原因となっているオブジェクトを特定します。

A.6.5.2 jmapコマンドを使った調査方法

  増加しているクラスの種類は、jmapコマンドを使って調べることもできます。jmapコマンドを次のように実行します。
  実際には、標準出力をリダイレクトするなどして、結果をファイルなどに保存してください。

jmap -histo:live <プロセスID>

24 jmapコマンドの実行例

  このとき、「-histo」オプションに「live」サブオプションを付けてください。「:live」を付けると、ヒストグラムを採取する前にGCを実行します。つまり、GC直後の生存オブジェクトだけを含むヒストグラムを得られます。これは、メモリリークの調査において重要な点です。

  jmapコマンドを複数回実行したあとで、採取したヒストグラムを比較してください。オブジェクトが増加しているクラスがあれば、メモリリークしている可能性が考えられます。

  例えば、図25でそれぞれ15,923個、15,342個だったbyte配列とcom.fujitsu.demo.MemLeak$Blobが、図26の時点では184,406個と183,823個に増加しています。

num     #instances         #bytes  class name
----------------------------------------------
   1:         15923       16062672  [B
2: 3460 2772800 [I 3: 6393 673312 <constMethodKlass> 4: 6393 514456 <methodKlass> 5: 10802 481192 <symbolKlass> 6: 495 271336 <constantPoolKlass> 7: 15342 245472 com.fujitsu.demo.MemLeak$Blob
8: 2245 230552 [C 9: 495 195784 <instanceKlassKlass> 10: 469 182432 <constantPoolCacheKlass> :

25 jmapで採取したヒストグラムの例(1)


  このような場合は、これらのオブジェクトについて、メモリリークが発生しているかどうか調べていきます。

num     #instances         #bytes  class name
----------------------------------------------
   1:        184406      191283136  [B
2: 26142 23888848 [I 3: 183823 2941168 com.fujitsu.demo.MemLeak$Blob
4: 322 807792 [Ljava.lang.Object; 5: 6413 674944 <constMethodKlass> 6: 6413 516056 <methodKlass> 7: 10825 482120 <symbolKlass> 8: 497 272152 <constantPoolKlass> 9: 2257 231064 [C 10: 497 196504 <instanceKlassKlass>

26 jmapで採取したヒストグラムの例(2)

A.6.5.3 ヒープダンプの採取と分析

  ヒープダンプを採取して、com.fujitsu.demo.MemLeak$Blobクラスの参照関係を調べます。
Java監視機能の[ヒープ分析]タブで[ダンプを取得する]ボタンを押して、ヒープダンプを採取してください。

27 Java監視機能による、メモリリーク調査(ヒープダンプ採取)


  ヒープダンプが出力されたファイル名は、ボタンの右側に表示されます。このファイルを、jhatで解析します。


  ヒープダンプの出力(ファイル)は、アプリケーションが動作しているマシンに生成されます。


  ヒープダンプは、jmapコマンドを使って採取することもできます。その場合、「-dump」オプションに「live」サブオプションを付けて、生存オブジェクトだけを含むヒープダンプを採取してください。

次の図は、ローカルマシンにコピーしたヒープダンプを、jhatを起動する例です。

> jhat fjvm-heapdump.5216.0927164520

28 jhatの起動例


  jhatを起動したマシンに、インターネットブラウザでアクセスします。アクセスするポート番号は、デフォルトでは7000番です。

http://localhost:7000/object/0x10553ce0

29 インターネットブラウザでアクセスするURLの例

  次の図は、ローカルマシンで動作しているjhatに対して、Firefoxでアクセスしたときの画面の例です。

30 jhatによる調査(jhatのトップ画面)


  増加しているcom.fujitsu.demo.MemLeak$Blobクラスのリンクをクリックすると、そのクラスに関する情報が表示されます。

31 jhatによる調査(増加量が多いクラスの情報)


  次に、このクラスのインスタンスを表示するため、「Instances」の「Exclude subclasses」をクリックします。

32 jhatによる調査(増加量が多いクラスのインスタンス一覧)


  com.fujitsu.demo.MemLeak$Blobクラスのインスタンスの一覧が表示されました。一覧から、適当なインスタンスのリンクをクリックします。

33 jhatによる調査(オブジェクトの参照元の調査)


  インスタンスに関する情報が表示されました。

  com.fujitsu.demo.MemLeak$Blobオブジェクトを参照しているオブジェクトを特定するため、「References to this object」に表示されているリンクをクリックします。

34 jhatによる調査(オブジェクトの参照元の情報)


  com.fujitsu.demo.MemLeak$Blobオブジェクトの参照元は、巨大な配列であることが分かりました。ブラウザのスクロールバーを下に移動して「References to this object」を見ると、更に参照元があることが分かります。

35 jhatによる調査(配列の参照元の調査)


  このようにして「References to this object」のリンクを辿っていくと、com.fujitsu.demo.MemLeakのstatic変数blobsから参照していることが分かりました。

36 jhatによる調査(オブジェクトの参照元の調査)


  com.fujitsu.demo.MemLeakクラスのリンクをクリックして、クラスの情報を表示してみると、確かにstatic変数blobsが存在しました。この変数は、java.util.Listインタフェースを実装したクラスのインスタンスを指しています。

37 jhatによる調査(メモリリークの原因となっているstatic変数の例)


  つまり、このblobsにcom.fujitsu.demo.MemLeak$Blobを追加したあと、不要になった時点でオブジェクトをリストから削除しているかどうか、プログラムを確認する必要があります。

A.6.5.4 Java VisualVMを使った調査

  メモリリークは、Java VisualVMを使って調べることもできます。詳しくは、JDKドキュメントを参照してください。