Javaや.NETを使うと,メモリー管理を意識しなくてもプログラミングできる。例えばJavaの場合は,Java VM(Virtual Machine)が備える「ガベージ・コレクタ」と呼ぶメモリー管理機能が,未使用のオブジェクトを破棄してメモリーを解放するといった処理を自動的に実行する(図1)。

図1●JavaVMによるメモリー管理の仕組み
図1●JavaVMによるメモリー管理の仕組み
ヒープ領域は,生存期間の短いオブジェクトを格納する「New領域」と生存期間の長いオブジェクトを格納する「Old領域」で構成される。New領域はさらに,最初にオブジェクトを格納する領域「Eden」と,しばらく生存しているオブジェクトを格納する「世代0」「世代1」から成る。Edenに存在するオブジェクトは,生存期間が延びるにつれて,世代0または世代1に移動。世代0と世代1を何度か移動した後,最終的にOld領域に移される
[画像のクリックで拡大表示]

 だが,処理系のメモリー管理方法を意識せずに開発したプログラムは,「単体テストでは問題がなくても,ほかのプログラムと組み合わせたり負荷をかけたりすると,パフォーマンスのトラブルを引き起こしやすい」(富士通 TRIOLE基盤統括部 SI技術部 部長 大坪史郎氏)。こうしたトラブルは主に,未使用のメモリー領域を解放する「GC(ガベージ・コレクション)」処理によって引き起こされる。GCの実行中はプログラムの動作が停止するので,パフォーマンス上の問題になりやすい。

2種類のGCが発生する

 Javaを例に説明しよう。Java VMは,起動時に「ヒープ(Heap)」と呼ぶメモリー領域を確保し,クラスから生成したオブジェクトを格納する。J2SE1.3以降のJava VM(HotSpot)では,ヒープは二つのメモリー領域から成る。一つは生存期間の短いオブジェクトを格納する「New領域」,もう一つは生存期間の長いオブジェクトを格納する「Old領域」である。

 生成したオブジェクトは,まずNew領域に格納され,使用中の状態が続くと最終的にOld領域に移動する。実際には,New領域は「Eden」「世代(Survivor)0」「世代1」の3領域に分かれている。最初にオブジェクトを格納するのはEdenである。Edenがオブジェクトでいっぱいになると,「Scavenge GC」処理が実行される。使用中のオブジェクトだけを世代0か1の空いている方にコピーし,Edenの内容は削除される(これにより未使用だったメモリーが解放される)。

 一時的に使われたオブジェクトはScavenge GCで破棄されるが,継続して使われているオブジェクトは,Scavenge GCのたびに世代0と世代1の間で繰り返しコピーされる。その回数が「MaxTenuringThreshold」の値(デフォルトでは32回)を超えたオブジェクトが,Old領域に移される。

 これを繰り返すうちに,New領域とOld領域の両方がいっぱいになることがある。するとガベージ・コレクタは,今度は「Full GC」と呼ぶ処理を実行する。Full GCは,New領域とOld領域に対するメモリー解放処理である。

Full GCの抑制を第一に

 Scavenge GCやFull GCの実行中はプログラムの動作が停止するので,パフォーマンスは低下する。特にFull GCは,対象となるメモリー領域が広いうえに,不連続の未使用領域(フラグメンテーション)を抑える仕組みが複雑なので,実行に時間がかかる。Scavenge GCが1回当たり数ミリ~数百ミリ秒で済むのに対し,Full GCは数百ミリ~数秒もかかる。それゆえ,Full GCを可能な限り抑制するように意識したい(図2)。

図2●ヒープ領域を考慮したチューニングのポイント
図2●ヒープ領域を考慮したチューニングのポイント
パフォーマンスに与えるインパクトが大きいFull GCを抑制することが重要。そのうえで,Scavenge GCを減らすことを考慮する。負荷テストやロングラン・テストを通じて各チューニング項目の最適値を探り出す必要がある
[画像のクリックで拡大表示]

 Full GCを抑制するには,Old領域に移動するオブジェクトの数を減らすことが重要だ。そのために,オブジェクトを過度に使い回すのは避けたい。もちろん,新規生成を繰り返してNew領域を浪費し,Scavenge GCを頻発させないように,バランスは考慮する必要がある。同時に,Full GCやScavenge GCが発生しにくいように,ヒープ内の各領域のメモリー容量を調整する。メモリー容量は,Javaコマンドの起動オプションで設定できる(図2の下)。目安としてFull GCの発生が5~10分ごと,Scavenge GCの発生が5~10秒ごとになるようにチューニングする。