ここでは,ガーベージコレクションの仕組みについて説明します。
ガーベージコレクションは,プログラムが使用し終わったメモリ領域を自動的に回収して,ほかのプログラムが利用できるようにするための技術です。
ガーベージコレクションの実行中は,プログラムの処理が停止します。このため,ガーベージコレクションを適切に実行できるかどうかが,システムの処理性能に大きく影響します。
プログラムの中でnewによって作成されたJavaオブジェクトは,JavaVMが管理するメモリ領域に格納されます。Javaオブジェクトが作成されてから不要になるまでの期間を,Javaオブジェクトの寿命といいます。
Javaオブジェクトには,寿命の短いオブジェクトと寿命の長いオブジェクトがあります。サーバサイドで動作するJavaアプリケーションの場合,リクエストやレスポンス,トランザクション管理などで,多くのJavaオブジェクトが作成されます。これらのJavaオブジェクトは,その処理が終わると不要になる,寿命が短いオブジェクトです。一方,アプリケーションの動作中使用され続けるJavaオブジェクトは,寿命が長いオブジェクトです。
効果的なガーベージコレクションを実行するためには,寿命の短いオブジェクトに対してガーベージコレクションを実行して,効率良くメモリ領域を回収することが必要です。また,繰り返し使用される寿命の長いオブジェクトに対する不要なガーベージコレクションを抑止することが,システムの処理性能の低下防止につながります。これを実現するのが,世代別ガーベージコレクションです。
世代別ガーベージコレクションでは,Javaオブジェクトを,寿命が短いオブジェクトが格納されるNew領域と,寿命が長いオブジェクトが格納されるTenured領域に分けて管理します。New領域はさらに,newによって作成されたばかりのオブジェクトが格納されるEden領域と,1回以上のガーベージコレクションの対象になり,回収されなかったオブジェクトが格納されるSurvivor領域に分けられます。New領域内で一定回数以上のガーベージコレクションの対象になったJavaオブジェクトは,長期間必要なJavaオブジェクトと判断され,Tenured領域に移動します。
世代別ガーベージコレクションで管理するメモリ空間とJavaオブジェクトの概要を次の図に示します。
図7-1 世代別ガーベージコレクションで管理するメモリ空間とJavaオブジェクトの概要
世代別ガーベージコレクションで実行されるガーベージコレクションには,次の2種類があります。
一般的に,コピーガーベージコレクションの方が,フルガーベージコレクションよりも短い時間で処理できます。
次に,ガーベージコレクションの処理について,あるJavaオブジェクト(オブジェクトA)を例にして説明します。
このようにしてオブジェクトが移動することで,Tenured領域の使用サイズは増加します。Tenured領域を一定サイズまで使うと,フルガーベージコレクションが発生します。
JavaVMのチューニングでは,JavaVMオプションでそれぞれのメモリ空間のサイズや割合を適切に設定することで,不要なオブジェクトがTenured領域に移動することを抑止します。これによって,フルガーベージコレクションが頻発することを防ぎます。
オブジェクトがコピーガーベージコレクションの対象になった回数をオブジェクトの年齢といいます。
オブジェクトの寿命と年齢の関係を次の図に示します。
図7-2 オブジェクトの寿命と年齢の関係
アプリケーションが開始して初期化処理が完了したあとで,何度かのコピーガーベージコレクションが実行されると,長期間必要になる寿命の長いオブジェクトはTenured領域に移動します。このため,アプリケーションの開始後しばらくすると,Javaヒープの状態は安定し,作成されるJavaオブジェクトとしては,寿命が短いオブジェクトが多くなります。特に,New領域のチューニングが適切にできている場合,Javaヒープが安定したあとの大半のオブジェクトは,1回目のコピーガーベージコレクションで回収される,寿命が短いオブジェクトになります。
JavaVMでは,コピーガーベージコレクションの対象になるNew領域のメモリ空間を,Eden領域,Survivor領域に分けて管理します。さらに,Survivor領域は,From空間とTo空間に分けられます。From空間とTo空間は,同じメモリサイズです。
New領域の構成を次の図に示します。
図7-3 New領域の構成
Eden領域は,newによって作成されたオブジェクトが最初に格納される領域です。プログラムでnewが実行されると,Eden領域のメモリが確保されます。
Eden領域がいっぱいになると,コピーガーベージコレクションが実行されます。コピーガーベージコレクションでは,次の処理が実行されます。
この結果,Eden領域とTo空間は空になり,使用中のオブジェクトはFrom空間に存在することになります。
コピーガーベージコレクション実行時に発生するオブジェクトの移動を次の図に示します。
図7-4 コピーガーベージコレクション実行時に発生するオブジェクトの移動
このようにして,使用中のオブジェクトは,コピーガーベージコレクションが発生するたびに,From空間とTo空間を行ったり来たりします。ただし,寿命の長いオブジェクトを行き来させ続けると,コピー処理の負荷などが問題になります。これを防ぐために,From空間とTo空間でJavaオブジェクトを入れ替える回数にしきい値を設定して,年齢がしきい値に達したJavaオブジェクトはTenured領域に移動させるようにします。
年齢がしきい値に達していないJavaオブジェクトがTenured領域に移動することを,退避といいます。退避は,コピーガーベージコレクション実行時にEden領域およびFrom空間で使用中のオブジェクトが多くなり,移動先であるTo空間のメモリサイズが不足する場合に発生します。この場合,To空間に移動できなかったオブジェクトが,Tenured領域に移動します。
図7-5 オブジェクトの退避
オブジェクトの退避が発生した場合,Tenured領域に本来格納されないはずの寿命の短いオブジェクトが格納されます。これが繰り返されると,コピーガーベージコレクションで回収されるはずのオブジェクトがメモリ空間に残っていくため,Javaヒープのメモリ使用量が増加していき,最終的にはフルガーベージコレクションが発生します。
オブジェクトの退避が発生した場合のメモリ使用量の変化について,次の図に示します。
図7-6 オブジェクトの退避が発生した場合のメモリ使用量の変化
フルガーベージコレクションでは,システムが数秒から数十秒停止することがあります。
したがって,メモリ空間の構成とメモリサイズを検討するときには,オブジェクトの退避が発生しないように,Eden領域とSurvivor領域のバランスを検討する必要があります。
日立のJavaVMでは,Eden領域,Survivor領域,およびTenured領域以外の領域としてExplicitヒープという領域を利用することができます。Explicitヒープ領域はガーベージコレクション対象外の領域です。
Explicitヒープ領域に格納するオブジェクトは,自動配置設定ファイル,および明示管理ヒープ機能APIを使って指定します。指定されたオブジェクトは,Survivor領域からTenured領域へ移動する際にExplicitヒープ領域へ移動します。コピーガーベージコレクションで回収の対象にならない長寿命オブジェクトを指定することで,Tenured領域のメモリ使用量を少なくし,フルガーベージコレクションの発生を抑止します。また,明示管理ヒープ機能の自動配置設定ファイルや,明示管理ヒープ機能APIを使って,指定したオブジェクトをExplicitヒープ領域に生成することもできます。
明示管理ヒープ機能の詳細については,マニュアル「Cosminexus アプリケーションサーバ 機能解説 拡張編」の「8. 明示管理ヒープ機能を使用したフルガーベージコレクションの抑止」を参照してください。