Java開発の問題解決を助ける(2)
プロファイラでメモリリークとパフォーマンス問題を解決

サン・マイクロシステムズ
岡崎 隆之
2005/8/10

 この連載は、Java開発を妨げるさまざまな問題の解決方法を扱います。前回はプログラムのバグを効率よく発見し解決する方法を紹介しました。第2回は、プロファイラを使ってメモリリークやパフォーマンスの問題を解決する手法を紹介します。

メモリリーク(メモリの無駄遣い)を探せ!

 メモリリークとは不要になったオブジェクトが何らかの理由で解放されず、メモリ領域を無駄に占拠してしまっている状況です。このような不要なオブジェクトが積もり積もっていくと、メモリ不足でシステムが停止してしまうなどの深刻な問題を引き起こす場合があります。ではこのような深刻な問題を引き起こしてしまうメモリリークの見つけ方、解決方法を学んでいきましょう。

どうしてメモリリークは起こるのか

 では、そもそもどうしてメモリリークが起こってしまうのでしょうか。Java仮想マシンは不要になったオブジェクトはガーベジコレクション処理によって自動的に解放します。ガーベジコレクション処理はオブジェクトが不要になったかそうでないかを、誰からも参照されていないことを判断基準にオブジェクトを開放していきます。この処理はほとんどの場合うまく動作するのですが、「ビジネスロジックから見て不要になったにもかかわらず参照が残っている」場合にはJava仮想マシンはその参照先のオブジェクトが不要になったことを判断できずメモリを開放することができません。これにより、メモリリークが発生してしまうのです。

メモリリークを見つけるには

 メモリリークが発生しているかどうかは、不要なオブジェクトがガーベジコレクション処理でメモリ上から開放されているかどうかを確認することで判断します。何回かガーベジコレクション処理が実行されているにもかかわらず開放されていないオブジェクトはどこかからか参照されている可能性が高く、メモリを無駄遣いしているオブジェクトの可能性があります。 最終的にそのオブジェクトがメモリを無駄遣いしているかどうかを判断するには、そのビジネスロジックの処理内容から判断する必要がありますが、オブジェクトの種類やそのオブジェクトを生成したコードを絞り込むことでメモリリークを起こしているコードを効率よく見つけることができます。

プロファイラを使ってみよう

 メモリリークの原因の絞り込みを効率的に行うためにはプロファイラ・ツールを用います。プロファイラ・ツールはメモリ中に確保されているオブジェクトのサイズや個数、オブジェクトが生成された場所といった情報や、メソッドの実行時間などを統計的な情報として取得することができるツールです。では、実際にサンプルプログラムを例にしてこのプロファイラ・ツールを使った問題の切り分けを行ってみましょう。 サンプルプログラムのコードはここ(sample.zip)からダウンロードできます。今回は無償で利用でき、Windows、LinuxやSolarisに対応したプロファイラである「NetBeans Profiler(http://profiler.netbeans.org/)」を使用します。

メモリリークを含んだサンプルプログラム

 サンプルプログラムは実行されているディレクトリ中のテキストファイルを再帰的に読み込んで、テキストファイル中に含まれる空白や改行で区切られた単語の数を数えます。例えば以下のようなファイルを読み込みます。

読み込むファイルの例(JDK 1.5に含まれるXusage.txtより抜粋)
   -Xmixed      mixed mode execution(default)
  -Xint          interpreted mode execution only
  -Xbootclasspath:
                    set search path for bootstrap classes and resources
  -Xbootclasspath/a:
                    append to end of bootstrap class path

 このファイルの-Xmixedやmixed、modeといった空白で区切られた部分が単語としてカウントされます。実行すると、以下のように結果が表示されます。

C:\Program Files\Java\jdk1.5.0_04\jre\bin\client\Xusage.txt は 160ワードでした.
 重複しないワードは 110ワードでした.
C:\Program Files\Java\jdk1.5.0_04\jre\bin\server\Xusage.txt は 160ワードでした.
 重複しないワードは 110ワードでした.
C:\Program Files\Java\jdk1.5.0_04\jre\lib\jvm.hprof.txt は 372ワードでした. 重
複しないワードは 205ワードでした.
C:\Program Files\Java\jdk1.5.0_04\jre\README.txt は 1599ワードでした. 重複しな
いワードは 705ワードでした.
C:\Program Files\Java\jdk1.5.0_04\jre\THIRDPARTYLICENSEREADME.txt は 10105ワードした. 重複しないワードは 1944ワードでした.

 では、このサンプルプログラムの問題点をプロファイラを使って検証してみましょう。

開発環境とプロファイラの準備

 まず開発環境とプロファイラをインストールします。

  1. 統合開発環境であるNetBeans 4.1をダウンロードページ(http://www.netbeans.org/downloads/index.html)からダウンロードし、インストールしてください。

  2. 次にプロファイラをダウンロードします。NetBeans Profilerのダウンロードページ(http://profiler.netbeans.org/download.html)から、それぞれのプラットフォームに合ったNetBeans Profilerのインストーラをダウンロードしてください。ダウンロードページにはAdditional DownloadとしてJ2EEサーバに対するJDKなどが紹介されていますが、今回は使用しません。

  3. ダウンロードしたNetBeans Profilerのインストーラを実行します。NetBeans Profilerのインストーラは途中でNetBeansのインストールされているディレクトリを聞いてくるので、そのディレクトリを選択してインストールを続行します(例えばWindowsでNetBeans 4.1を標準インストールした場合は「C:\Program Files\netbeans-4.1」を選択します)。
画面1 インストール済みNetBeansの選択

 次に、サンプルプログラムを実行できるようにプロジェクトを作成します。

  1. サンプルプログラム(sample.zip)を適当なディレクトリに展開します。

  2. NetBeansを起動し、メニューの[File]から[New Project]を選びます。

  3. プロジェクトのカテゴリ[General]にある[Java Project with Existing Sources]を選択し、次に進みます。

  4. プロジェクト名を入力して次に進みます。

  5. [Source Package Folders]にある[Add Folder]をクリックし、サンプルプログラムを展開したディレクトリを選び、[Finish]を押します。これでプロジェクトの作成が完了します。
画面2 プロジェクトの作成を終えたところ

 これでサンプルプログラムの実行準備が整いました。

まずは現象を把握する


 メニューの[Profile]から[Profile Main Project]を選ぶとプロファイル対象のメインクラスを選択するように指示されるので、ここではWordCounterクラスを選択します。次に、プロファイリングタスクを選択する画面が出てきますが、今回はメモリについてのプロファイルを行うので[Analyze Memory Usage]をクリックします。メモリリークを探す場合にはガーベジコレクションの情報が非常に重要な情報となるので、この情報を取得するために[Record both object creation and garbage collection]を選択します。さらに[Record Stack Trace for Allocations]にチェックを入れ、プログラム中のどこでオブジェクトが生成されたかを記録するようにします。

画面3 プロファイリングタスクの選択で選択後の画面

 プロファイル結果はメニューの[Window]にある[Profiling]メニューの[Live Results]を選択することで取得することができます。

画面4 プロファイル結果

 このプロファイル結果を見る際、最も重要な項目は右端のGenerationsの項目です。Generationsは何世代(ガーベジコレクション処理が1回起こるたびにオブジェクトの世代が1つ上がる)生き残ったかを示す値で、この数値が増加傾向にあるクラスはメモリリークの可能性が非常に高いと考えられます。メモリを無駄遣いしている可能性が高いクラスはbyte[]、String、char[]、WordCountといったクラスであることが推測できます。

1/3

 INDEX

第2回 Java開発の問題解決を助ける
Page1
メモリリーク(メモリの無駄遣い)を探せ!
プロファイラを使ってみよう
  Page2
メモリの無駄を作っている部分を特定する
パフォーマンスボトルネックを探せ!
  Page3
プロファイラでパフォーマンスの問題を解決










Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間