Javaはどのように動くのか~図解でわかるJVMの仕組み

第6回HotSpot JVMのヒープ構造の仕組みを把握する

ヒープ構造は2つの領域から成り立つ

前回までの連載では、HotSpot JVM(以下HotSpot)やJRockit JVMなどのJVMの種類を問わない内容を紹介しました。今回からは、Oracle社より提供されるHotSpotをもとに、実装の具体的な特徴を見ていきましょう。

HotSpotは無料で使用できるJVMの1つで、デスクトップからサーバまで幅広い環境で使用されています。フルGCの発生回数を抑えてアプリケーションの停止時間を短くするために、第5回で紹介した世代別GCを採用しています。

HotSpotでは、ヒープを以下の2つの領域に分かれています。

  • New領域 ⇒ 若い世代の領域
  • Tenured領域 ⇒ 古い世代の領域
図1 HotSpotでの世代別ヒープ
図1 HotSpotでの世代別ヒープ

比較的短命なオブジェクトは、New領域内のGCで回収されます。長時間使用されるオブジェクトは、New領域を対象とされるマイナーGCを経て、Tenured領域へと移動していきます(詳細は次回解説します⁠⁠。

図2 マイナーGCによってTenured領域に移動
図2 マイナーGCによってTenured領域に移動

オブジェクトの寿命をふまえて3つのNew領域を使い分ける

さらにHotSpotは、単純な世代別ヒープだけではなく、New領域を以下の3つに分けてコピーGCを行うのが特徴です。

  • Eden領域
  • Survivor 0領域
  • Survivor 1領域
図3 HotSpotのヒープ構造
図3 HotSpotのヒープ構造

これは、多くのアプリケーションで生成されるほとんどのオブジェクトが、ほんのわずかな時間だけ使用される非常に短命なものである点に着目した結果です。

図4 HotSpotのヒープ構造とオブジェクトの寿命の理想的な関係
図4 HotSpotのヒープ構造とオブジェクトの寿命の理想的な関係

HotSpotでは、オブジェクトの寿命に応じて、以下のように格納する領域を使い分けます。

  • 短命なオブジェクトの格納先 ⇒ Eden領域
  • 中期間生きているオブジェクトの格納先 ⇒ Survivor領域
  • 長期間生きているオブジェクトの格納先 ⇒ Tenured領域
図5 オブジェクトの寿命
図5 オブジェクトの寿命

New領域を一般的なコピーGCで実現したとすると、New領域をFrom領域とTo領域の2つに分けるため、アプリケーションはNew領域の半分を使用できなくなります。

そのうえ、半分に分割されたヒープには、中期間生きてるオブジェクトが存在します。そのため、短命なオブジェクトが割り当てられる領域は、New領域の半分以下となってしまいます。

図6 一般的なコピーGCのヒープの使い方
図6 一般的なコピーGCのヒープの使い方

それに対して、HotSpotはEden領域をFrom領域として、2つのSurvivor領域をFrom領域もしくはTo領域として切り替えて使用します。

図7 Survivor領域の使われ方
図7 Survivor領域の使われ方

Survivor領域1つあたりのサイズは、JVM起動時のVM引数によって変更できます。HotSpotの仕様上、Survivor領域は、Eden領域と同じサイズまでしか設定できません。しかし、中期間生きているオブジェクトは、短命なオブジェクトに比べて少ないことが一般的なため、問題は起こりません。

図8 New領域中のEden領域とSurvivor領域のサイズ
図8 New領域中のEden領域とSurvivor領域のサイズ

その結果、Survivor領域の1つが使用できなくなっても、アプリケーションが使えない領域は全体の1/3よりも小さくなります。そして、短命なオブジェクトを格納するEden領域は、一般的なコピーGCで作成できる短命なオブジェクトの量よりも大きくなります。

図9 HotSpotではアプリケーションが使えない領域が少なくなる
図9 HotSpotではアプリケーションが使えない領域が少なくなる

このような仕組みのおかげで、HotSpotではアプリケーションがNew領域を最大限に使用できるのです。

次回は、このヒープ構造の中でオブジェクトがどのように割り当てられていくのかを見ていきましょう。

おすすめ記事

記事・ニュース一覧