ヒープ構造は2つの領域から成り立つ
前回までの連載では、HotSpot JVM(以下HotSpot)やJRockit JVMなどのJVMの種類を問わない内容を紹介しました。今回からは、Oracle社より提供されるHotSpotをもとに、実装の具体的な特徴を見ていきましょう。
HotSpotは無料で使用できるJVMの1つで、デスクトップからサーバまで幅広い環境で使用されています。フルGCの発生回数を抑えてアプリケーションの停止時間を短くするために、第5回で紹介した世代別GCを採用しています。
HotSpotでは、ヒープを以下の2つの領域に分かれています。
- New領域 ⇒ 若い世代の領域
- Tenured領域 ⇒ 古い世代の領域
比較的短命なオブジェクトは、New領域内のGCで回収されます。長時間使用されるオブジェクトは、New領域を対象とされるマイナーGCを経て、Tenured領域へと移動していきます(詳細は次回解説します)。
オブジェクトの寿命をふまえて3つのNew領域を使い分ける
さらにHotSpotは、単純な世代別ヒープだけではなく、New領域を以下の3つに分けてコピーGCを行うのが特徴です。
- Eden領域
- Survivor 0領域
- Survivor 1領域
これは、多くのアプリケーションで生成されるほとんどのオブジェクトが、ほんのわずかな時間だけ使用される非常に短命なものである点に着目した結果です。
HotSpotでは、オブジェクトの寿命に応じて、以下のように格納する領域を使い分けます。
- 短命なオブジェクトの格納先 ⇒ Eden領域
- 中期間生きているオブジェクトの格納先 ⇒ Survivor領域
- 長期間生きているオブジェクトの格納先 ⇒ Tenured領域
New領域を一般的なコピーGCで実現したとすると、New領域をFrom領域とTo領域の2つに分けるため、アプリケーションはNew領域の半分を使用できなくなります。
そのうえ、半分に分割されたヒープには、中期間生きてるオブジェクトが存在します。そのため、短命なオブジェクトが割り当てられる領域は、New領域の半分以下となってしまいます。
それに対して、HotSpotはEden領域をFrom領域として、2つのSurvivor領域をFrom領域もしくはTo領域として切り替えて使用します。
Survivor領域1つあたりのサイズは、JVM起動時のVM引数によって変更できます。HotSpotの仕様上、Survivor領域は、Eden領域と同じサイズまでしか設定できません。しかし、中期間生きているオブジェクトは、短命なオブジェクトに比べて少ないことが一般的なため、問題は起こりません。
その結果、Survivor領域の1つが使用できなくなっても、アプリケーションが使えない領域は全体の1/3よりも小さくなります。そして、短命なオブジェクトを格納するEden領域は、一般的なコピーGCで作成できる短命なオブジェクトの量よりも大きくなります。
このような仕組みのおかげで、HotSpotではアプリケーションがNew領域を最大限に使用できるのです。
次回は、このヒープ構造の中でオブジェクトがどのように割り当てられていくのかを見ていきましょう。