-
Notifications
You must be signed in to change notification settings - Fork 38
/
heap.re
177 lines (108 loc) · 8.76 KB
/
heap.re
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
= ヒープ構造
本章ではG1GCのVMヒープ構造について説明します。
== VMヒープ
HotspotVMのVMヒープは大きく次の2つの部分にわかれます。
1. 選択したGC用のメモリ領域
2. パーマネント(Permanent)領域
//image[vm_heap][VMヒープの全体像]
HotspotVMは言語利用者によってGCアルゴリズムが選択されると、そのGCに適した構造のメモリ領域を作成します。
それが1.です。このメモリ領域に対して、選択したGCの対象となるオブジェクトを割り当てていきます。
2.のパーマネント領域のPermanentには「永続的な」という意味があります。
この名前のとおり、パーマネント領域には型情報やメソッド情報などの長生きするオブジェクトが割り当てられます。
GCアルゴリズムの違いでパーマネント領域の構造が変わることはほぼありません。
=== VMヒープクラスの初期化
すべてのVMヒープクラスは@<code>{CollectedHeap}クラスを継承しています。
//source[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;
//}
@<code>{CollectedHeap}は上記のようにさまざまインタフェースを持っています。
適切なVMヒープクラスは@<code>{Universe::initialize_heap()}で選ばれます。
//source[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ヒープクラスの対応については@<hd>{abstract|CollectorPolicyクラス}ですでに述べました。
ここでは、適切なVMヒープクラスのインスタンスが生成され、@<code>{Universe::_collectedHeap}に格納されたあと、最終的に@<code>{initialize()}を呼び出す点を抑えておいてください。
@<code>{Universe}クラスは次に示す通り@<code>{AllStatic}クラスを継承したクラスです。
//source[share/vm/memory/universe.hpp]{
113: class Universe: AllStatic {
201: static CollectedHeap* _collectedHeap;
346: static CollectedHeap* heap() { return _collectedHeap; }
//}
@<code>{Universe::heap()}を呼び出すことで、@<code>{Universe::initialize_heap()}で選択した適切なVMヒープのインスタンスを取得できます。
== G1GCヒープ
G1GCのヒープは『アルゴリズム編 1.2 ヒープ構造』で示したとおり、一定のサイズのリージョンに区切られています。
ここではG1GCヒープがリージョンをどのように保持しているかを見ていきましょう。
=== G1CollectedHeapクラス
@<code>{G1CollectedHeap}クラスは非常に多くの役割を担っていますが、一度に説明しても混乱するだけですので、ここでは@<code>{G1CollectedHeap}クラスの主要な3つのメンバ変数の紹介に留めておきます。
* @<code>{_hrs} - すべての@<code>{HeapRegion}を配列によって保持
* @<code>{_young_list} - 新世代の@<code>{HeapRegion}リスト
* @<code>{_free_region_list} - 空き@<code>{HeapRegion}リスト
//image[g1gc_heap][G1GCヒープの構造]
各リージョンは@<code>{HeapRegion}というクラスで管理されています。
@<code>{G1CollectedHeap}クラスは@<code>{HeapRegionSeq}インスタンスへのポインタを格納する@<code>{_hrs}メンバ変数を持っています。
@<code>{HeapRegionSeq}の@<code>{_regions}メンバ変数には、G1GCヒープのすべてのリージョンに対応する@<code>{HeapRegion}のアドレスが配列によって格納されています。
@<code>{HeapRegion}は@<code>{G1CollectedHeap}クラスの@<code>{_young_list}、@<code>{_free_region_list}によって片方向リストでつながれています。
新世代の@<code>{HeapRegion}は@<code>{_young_list}につながれています。
空のリージョンと対応する@<code>{HeapRegion}は空きリージョンリスト(@<code>{_free_region_list})によってつながれています。
そして、旧世代の@<code>{HeapRegion}は何のリンクにもつながれていません。
=== HeapRegionクラス
@<code>{HeapRegion}は@<code>{_bottom}、@<code>{_end}メンバ変数をもち、それぞれリージョンの先頭アドレス、終端アドレスを格納しています。
//image[heap_region][HeapRegion]
@<code>{HeapRegion}クラスの継承図を@<img>{heap_region_hierarchy}に示します。
//image[heap_region_hierarchy][HeapRegion継承図]
継承関係が深いですが、そのすべてをおぼえる必要はありません。
「さまざまなクラスから機能を受け継いでいる」ということがわかればOKです。
@<code>{HeapRegion}クラスは@<code>{_bottom}・@<code>{_end}の他に@<code>{_top}というメンバ変数を持ちます。
@<code>{_top}はリージョン内の空きメモリ領域の先頭アドレスを保持します。
これらの@<code>{_bottom}・@<code>{_end}・@<code>{_top}は@<code>{Space}クラスに定義されているメンバ変数です。
@<code>{HeapRegion}クラスには次の片方向リスト用のメンバ変数が定義されています。
1. @<code>{_next_young_region}
2. @<code>{_next_in_special_set}
1.は名前のとおり、次の新世代リージョンを指します。
2.の@<code>{_next_in_special_set}メンバ変数はリージョンが所属する集合によって意味の違うさまざまなリージョンを指します。
具体的に言えば、リージョンがフリーリージョンリストに所属するときには、次の空リージョンがつながれ、回収集合のリストに所属するときには、次のGC対象である使用中のリージョンがつながれます。
また、@<code>{_next_in_special_set}メンバ変数にリージョンをつなぐとき、@<code>{_next_in_special_set}メンバ変数が何の用途に使われているかを覚えておくため、「このリージョンはこの集合に所属しています」というフラグを立てておきます。
フラグに使用する主要なメンバ変数は次のとおりです。
これらのフラグはすべて@<code>{bool}型です。
* @<code>{_in_collection_set} - 回収集合内のリージョン
* @<code>{_is_gc_alloc_region} - 前回の退避後にオブジェクトが割り当てられたリージョン
=== HeapRegionSeqクラス
@<code>{HeapRegionSeq}クラスはHotspotVMが独自に実装している@<code>{GrowableArray}という配列を表現するクラスをラップする形で定義されています。
//source[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行目の@<code>{_regions}メンバ変数がリージョンに対応するを保持する配列(@<code>{GrowableArray}クラス)です。
@<code>{GrowableArray}クラスは通常の配列と違い、要素を追加する際に配列を拡張する処理が実装されています。
名前のとおり、増大可能な(Growable)配列なのです。
@<code>{_regions}に格納される@<code>{HeapRegion}の配列は、リージョンの先頭アドレスの昇順にソートされた状態を保ちます。
63行目の@<code>{insert()}メンバ関数で@<code>{_regions}に新しいリージョンのアドレスを追加します。
この@<code>{insert()}で配列のソートをおこないます。
70行目の@<code>{at()}メンバ関数は指定したインデックスのリージョンを返します。
== パーマネント領域
G1GCのパーマネント領域は@<code>{CompactingPermGenGen}クラスによって管理されています。
@<code>{g1CollectedHeap}クラスの@<code>{_perm_gen}というメンバ変数に@<code>{CompactingPermGenGen}インスタンスへのポインタが格納されます。
パーマネント領域はG1GCではなく、マークコンパクトGCの対象となります。そのため、本章では詳細な説明は行いません。