JVMとGCのしくみ

先日職場でJVMの話をしてた。
ちょうどいい機会だからちょっとまとめたいと思う。

JVMの構成

まずはJVMの構成について。JVMには3つの領域が存在する。

  • Permanent領域(非ヒープ領域)
  • New領域(ヒープ領域)
  • Old領域(ヒープ領域)

Permanent領域にはJVMにロードされたクラスやメソッドの情報、New領域にはインスタンス化されたオブジェクトの情報、Old領域には寿命の長いオブジェクトの情報が管理される。(「寿命の長い」については後述のScavenge GCを参照。)
Permanent領域は非ヒープ領域、New領域とOld領域はヒープ領域となる。
非ヒープ領域には基本的にGCは走らず、JVM起動時に静的な情報が管理される。(
一方、ヒープ領域はインスタンス化されたオブジェクト情報といった動的な情報が管理され、GC対象となる。

※ユーザ定義のクラスローダーが存在する場合、そのクラスローダーがロードした情報はGC対象となる。少し古い情報だが、HotSpot VMの特性を知る (2/2):Javaパフォーマンスチューニング(6) - @ITの「Permanent領域とクラスローダ」が分かりやすい。

New領域をもうちょっと詳しく

New領域内は3つに分割されており、それぞれ以下の役割を担う。

  • Eden領域 ・・・ オブジェクトがインスタンス化された際に、はじめに配置される領域
  • From領域 ・・・ Scavenge GC時のオブジェクト退避領域
  • To領域 ・・・ Scavenge GC時のオブジェクト退避領域

※Scavenge GCについては後述。


GCの仕組み

GCには以下の2つが存在する。

  • Scavenge GC ・・・ New領域のみを対象としたGCで、比較的短時間で処理が終了する。
  • Full GC ・・・ 主にOld領域とPermanent領域を対象としたGCで、処理に時間がかかる。

◆Scavenge GC

Scavenge GCはNew領域のみを対象としたGCであり、比較的頻繁に実施される。
実施される契機は「Eden領域が一杯になったら」であり、処理は短時間で終了する。

<こんな感じ>

  1. オブジェクトがインスタンス化され、Eden領域に配置される。
  2. Eden領域が一杯になるとScavenge GCが実行され、使用中のオブジェクトはTo領域へ移動、不要なオブジェクトは破棄される。
  3. Eden領域が再度一杯になると、2回目のScavenge GCが実行され、使用中のオブジェクトはTo領域へ移動、不要なオブジェクトは破棄、To領域のオブジェクトはFrom領域へ移動する。
  4. 2,3の処理を繰り返し、To領域、From領域間の移動回数が「MaxTenuringThreshold(デフォルト:32回)」を超過したオブジェクトはOld領域へ移動される。

◆Full GC

Full GCは主にOld領域とPermanent領域を対象としたGCである。
実施される契機は「Old領域が一杯になったら」または「Permanent領域が不足したら」であり、処理時間は結構長い。
これはFull GCではScavenge GCに加えてOld領域、Permanent領域と、すべての領域について不要なオブジェクトの破棄を実施するためである。
また、Full GC実行中はほぼシステム停止状態となってしまう。(すべてのオブジェクトを検査するため、メインの処理が実施出来ないため)

このようなことがあるので、考え方としては極力Full GCを抑えるようにすべきである。
Full GC を抑えるには以下の点に留意することが大切。

  • オブジェクトをできるだけ使い回さないこと ・・・ オブジェクトの寿命が長くなるため、いずれ Old 領域に割り当てられることになり、 それらの数が増えると Full GC が発生しやすくなるため。
  • 新しいオブジェクトを大量に使用する場合は、New 領域を大きめにとること ・・・ Old 領域に割り当てられるオブジェクトを少なくするため。
  • staticメソッドを多用しないこと ・・・ Permanent領域にロードされる情報が増加し、Full GCが発生しやすくなるため。


さいごに

今回この記事を書くのにこの辺を参考にした。