第3章 ヒープ構造

本章ではG1GCのVMヒープ構造について説明します。

3.1 VMヒープ

HotspotVMのVMヒープは大きく次の2つの部分にわかれます。

  1. 選択したGC用のメモリ領域
  2. パーマネント(Permanent)領域
VMヒープの全体像

図3.1: VMヒープの全体像

HotspotVMは言語利用者によってGCアルゴリズムが選択されると、そのGCに適した構造のメモリ領域を作成します。それが1.です。このメモリ領域に対して、選択したGCの対象となるオブジェクトを割り当てていきます。

2.のパーマネント領域のPermanentには「永続的な」という意味があります。この名前のとおり、パーマネント領域には型情報やメソッド情報などの長生きするオブジェクトが割り当てられます。GCアルゴリズムの違いでパーマネント領域の構造が変わることはほぼありません。

VMヒープクラスの初期化

すべてのVMヒープクラスはCollectedHeapクラスを継承しています。

share/vm/gc_interface/collectedHeap.hpp

53: class CollectedHeap : public CHeapObj {

286:   virtual bool is_permanent(const void *p) const = 0;

323:   inline static oop obj_allocate(KlassHandle klass, int size, TRAPS);

497:   virtual void collect(GCCause::Cause cause) = 0;

CollectedHeapは上記のようにさまざまインタフェースを持っています。

適切なVMヒープクラスはUniverse::initialize_heap()で選ばれます。

share/vm/memory/universe.cpp

882: jint Universe::initialize_heap() {
883:
884:   if (UseParallelGC) {

886:     Universe::_collectedHeap = new ParallelScavengeHeap();

891:   } else if (UseG1GC) {

893:     G1CollectorPolicy* g1p = new G1CollectorPolicy_BestRegionsFirst();
894:     G1CollectedHeap* g1h = new G1CollectedHeap(g1p);
895:     Universe::_collectedHeap = g1h;

900:   } else {

901:     GenCollectorPolicy *gc_policy;

         /* 省略: 適切なCollectorPolicyを選ぶ */

919:     Universe::_collectedHeap = new GenCollectedHeap(gc_policy);
920:   }
921:
922:   jint status = Universe::heap()->initialize();

       ...

GCアルゴリズムとVMヒープクラスの対応については「2.4 CollectorPolicyクラス」ですでに述べました。ここでは、適切なVMヒープクラスのインスタンスが生成され、Universe::_collectedHeapに格納されたあと、最終的にinitialize()を呼び出す点を抑えておいてください。

Universeクラスは次に示す通りAllStaticクラスを継承したクラスです。

share/vm/memory/universe.hpp

113: class Universe: AllStatic {

201:   static CollectedHeap* _collectedHeap;

346:   static CollectedHeap* heap() { return _collectedHeap; }

Universe::heap()を呼び出すことで、Universe::initialize_heap()で選択した適切なVMヒープのインスタンスを取得できます。

3.2 G1GCヒープ

G1GCのヒープは『アルゴリズム編 1.2 ヒープ構造』で示したとおり、一定のサイズのリージョンに区切られています。ここではG1GCヒープがリージョンをどのように保持しているかを見ていきましょう。

G1CollectedHeapクラス

G1CollectedHeapクラスは非常に多くの役割を担っていますが、一度に説明しても混乱するだけですので、ここではG1CollectedHeapクラスの主要な3つのメンバ変数の紹介に留めておきます。

G1GCヒープの構造

図3.2: G1GCヒープの構造

各リージョンはHeapRegionというクラスで管理されています。G1CollectedHeapクラスはHeapRegionSeqインスタンスへのポインタを格納する_hrsメンバ変数を持っています。HeapRegionSeq_regionsメンバ変数には、G1GCヒープのすべてのリージョンに対応するHeapRegionのアドレスが配列によって格納されています。

HeapRegionG1CollectedHeapクラスの_young_list_free_region_listによって片方向リストでつながれています。

新世代のHeapRegion_young_listにつながれています。空のリージョンと対応するHeapRegionは空きリージョンリスト(_free_region_list)によってつながれています。そして、旧世代のHeapRegionは何のリンクにもつながれていません。

HeapRegionクラス

HeapRegion_bottom_endメンバ変数をもち、それぞれリージョンの先頭アドレス、終端アドレスを格納しています。

HeapRegion

図3.3: HeapRegion

HeapRegionクラスの継承図を図3.4に示します。

HeapRegion継承図

図3.4: HeapRegion継承図

継承関係が深いですが、そのすべてをおぼえる必要はありません。「さまざまなクラスから機能を受け継いでいる」ということがわかればOKです。

HeapRegionクラスは_bottom_endの他に_topというメンバ変数を持ちます。_topはリージョン内の空きメモリ領域の先頭アドレスを保持します。これらの_bottom_end_topSpaceクラスに定義されているメンバ変数です。

HeapRegionクラスには次の片方向リスト用のメンバ変数が定義されています。

  1. _next_young_region
  2. _next_in_special_set

1.は名前のとおり、次の新世代リージョンを指します。

2.の_next_in_special_setメンバ変数はリージョンが所属する集合によって意味の違うさまざまなリージョンを指します。具体的に言えば、リージョンがフリーリージョンリストに所属するときには、次の空リージョンがつながれ、回収集合のリストに所属するときには、次のGC対象である使用中のリージョンがつながれます。

また、_next_in_special_setメンバ変数にリージョンをつなぐとき、_next_in_special_setメンバ変数が何の用途に使われているかを覚えておくため、「このリージョンはこの集合に所属しています」というフラグを立てておきます。

フラグに使用する主要なメンバ変数は次のとおりです。これらのフラグはすべてbool型です。

HeapRegionSeqクラス

HeapRegionSeqクラスはHotspotVMが独自に実装しているGrowableArrayという配列を表現するクラスをラップする形で定義されています。

share/vm/gc_implementation/g1/heapRegionSeq.hpp

34: class HeapRegionSeq: public CHeapObj {
35:

38:   GrowableArray<HeapRegion*> _regions;

56:  public:

63:   void insert(HeapRegion* hr);

70:   HeapRegion* at(size_t i) { return _regions.at((int)i); }

114: };

38行目の_regionsメンバ変数がリージョンに対応するを保持する配列(GrowableArrayクラス)です。

GrowableArrayクラスは通常の配列と違い、要素を追加する際に配列を拡張する処理が実装されています。名前のとおり、増大可能な(Growable)配列なのです。

_regionsに格納されるHeapRegionの配列は、リージョンの先頭アドレスの昇順にソートされた状態を保ちます。63行目のinsert()メンバ関数で_regionsに新しいリージョンのアドレスを追加します。このinsert()で配列のソートをおこないます。

70行目のat()メンバ関数は指定したインデックスのリージョンを返します。

3.3 パーマネント領域

G1GCのパーマネント領域はCompactingPermGenGenクラスによって管理されています。

g1CollectedHeapクラスの_perm_genというメンバ変数にCompactingPermGenGenインスタンスへのポインタが格納されます。

パーマネント領域はG1GCではなく、マークコンパクトGCの対象となります。そのため、本章では詳細な説明は行いません。


御意見・御感想・誤植の指摘などは@nari3もしくはauthorNari/g1gc-impl-book - GitHubまでお願いします。

Webサイトのトップページ

(C) 2011-2012 Narihiro Nakamura