needtec.sakura.ne.jp

 21章のお話
 オブジェクトヘッダ
• 型オブジェクトポインター(4byte, 8byte)
 型の構造体へのポンタ
• 同期ブロックインデックス(4byte, 8byte)
 ロックとかCOMで利用する
 フィールド
マネージヒープ
NextObjPtr
マネージヒープ
NextObjPtr
オブジェクトAを割り当てたい!
同期ブロックインデックス~
フィールドまでが入るようにする
同期ブロック
型ハンドル
フィールド
マネージヒープ
A
NextObjPtr
オブジェクトAを割り当てたら
NextObjPtrjがオブジェクトの直後まで
進む
マネージヒープ
A
B
NextObjPtr
マネージヒープ
A
B
C
NextObjPtr
オブジェクトの割り当ては単なる
ポインタの加算なので非常に速い
マネージヒープ
A
B
C
D
E
F
G
オブジェクトを割り当てようとしたが
十分なアドレス空間がのこっていない!
ガベージコレクション
実行!
H
NextObjPtr
 全てのスレッドを一時停止
• ガベージコレクション終わるまですべてのスレッ
ドがオブジェクトにアクセスできない
 GCマーキングフェイズ
• 使ってないオブジェクトを探す
 コンパクションフェイズ
• 不要なオブジェクトを削除して圧縮する
ルート:
フィールドや
変数
マネージヒープ
A
B
C
D
E
F
G
H
参照を表す
クラスの静的またはインスタンスフィールド、 NextObjPtr
メソッドの実引数、ローカル変数などで
参照型の変数を総称してルートと呼びます
ルート:
フィールドや
変数
マネージヒープ
A
0
B
0
C
0
D
0
E
F
0
0
G
0
全オブジェクトの同期ブロックインデックス
フィールドに含まれているビットに0を指定
⇒これはすべてのオブジェクトを
削除するという意味
H
0
NextObjPtr
ルート:
フィールドや
変数
マネージヒープ
A
1
B
0
C
1
D
1
E
F
0
1
ルートから直接参照されている
オブジェクトをマークします。
G
0
H
0
NextObjPtr
ルート:
フィールドや
変数
マネージヒープ
A
1
B
0
C
1
D
1
E
F
0
1
G
1
H
0
マークをする際に、もし他のオブジェクトを
NextObjPtr
参照している場合は、そのオブジェクト
もマークします。
この例だとDをマークするときはGもマークします
ルート:
フィールドや
変数
マネージヒープ
A
1
B
0
C
1
D
1
E
F
0
1
G
1
H
0
これでマーキングフェイズが完了です。
NextObjPtr
マークされたオブジェクトは到達可能
といいます。
マークされていないのは到達不能といいます。
ルート:
フィールドや
変数
マネージヒープ
A
1
B
0
C
1
D
1
E
F
0
1
マークされているオブジェクトによって
使用されているメモリを移動させて
メモリ上に連続するようにします。
G
1
H
0
NextObjPtr
ルート:
フィールドや
変数
マネージヒープ
A
C
C
D
F
G
オブジェクトを移動させる際に、
オブジェクトを参照しているルートなどは移動した
分のバイト数を引く必要があります。
ルート:
フィールドや
変数
マネージヒープ
A
C
D
F
G
マークされているすべてのオブジェクトに対して
行います。
 メモリの空容量を連続的にすることで、マ
ネージヒープ上でメモリの断片化がなくな
ります。
 OutOfMemoryExceptionの例外が発生し
ます。
アプリケーションはその例外をキャッチし
て回復を試みることができますが、ほとん
どのアプリケーションはやってないのでプ
ロセスが終了して、OSがプロセスが使用
していたメモリを解放します。
 メソッド内のオブジェクトの生存期間は最
後に参照したところまでです。
メソッドの終了時までじゃ
ないです。
static void Main(string[] args)
{
Timer t = new Timer(TimerCallback, null, 0,
2000);
// タイミングA
Console.ReadLine();
// オブジェクトtの参照
Console.WriteLine(t.ToString());
// タイミングB
Console.ReadLine();
}
static void Main(string[] args)
{
Timer t = new Timer(TimerCallback, null, 0,
2000);
// タイミングA
Console.ReadLine();
// オブジェクトtの参照
Console.WriteLine(t.ToString());
// タイミングB
Console.ReadLine();
}
オブジェクトt
は保障される
オブジェクトt
は保障されない
ガベージコレクションが
実行されたら消える
 なおDebugでビルドした場合、JITコンパ
イラが生存期間を恣意的にメソッドの最後
まで伸ばします
Releaseビルドと
Debugビルドで動きがかわるぞ!
がっでむ!
 CLRのGCは世代別ガレージコレクタを採
用している
 世代別GCは次のことを前提にしている
• オブジェクトが新しいほど、その生存期間は短い
• オブジェクトが古いほど、その生存期間は長い
• ヒープの一部分の回収はヒープ全体の回収より高
速である
マネージヒープ
A
B
C
D
E
世代0
新しく追加されるオブジェクトは常に
世代0に追加される。
マネージヒープ
A
B
C
D
E
世代0
しばらくしてオブジェクトCとEが到達不能となる
その後、ガベージコレクションが発生したとする。
マネージヒープ
A
B
世代1
D
世代0
ガベージコレクションの後に世代0で生き残った
オブジェクトが世代1に移動して世代0は空になる
マネージヒープ
A
B
世代1
D
F
G
H
世代0
新しいオブジェクトは世代0に割り当てられていく。
世代0の予約サイズを超えた場合に
ガベージコレクションが実行される。
マネージヒープ
A
B
世代1
D
F
H
世代0
世代0のオブジェクトのみが検査され、
生き残ったオブジェクトは世代1に移動する。
世代1は検査していないので、オブジェクトBは
生き残る
マネージヒープ
A
B
世代1
D
F
H
I
世代0
J
K
L
マネージヒープ
A
B
世代1
D
F
H
I
L
世代0
ガベージコレクションを実行していくと
このように世代1が徐々に増加していく。
マネージヒープ
A
B
D
F
H
I
L
世代1
世代1のサイズが上限を超えた時に
ガベージコレクション
が発生したとしよう
M
N
世代0
O
マネージヒープ
A
D
世代2
L
M
O
世代1
世代0
世代1~世代0のオブジェクトを検査する。
世代1の到達可能オブジェクトは世代2となる。
世代0の到達可能オブジェクトは世代1となる
 世代別にGCを行うので、すべてのオブ
ジェクトを検査しなくてすむ。
 世代0の使用量が予約サイズを超えた
• 予約サイズはCLRが動的に決める
 System.GCをコードで実行
 Windowsが空容量低下の状況を報告
 CLRは個々のオブジェクトをスモールオブ
ジェクトかラージオブジェクトのどちらか
であると見なす
• 現在85,000バイト以上をラージオブジェクト
※ただし、変更される可能性あり
 ラージオブジェクトはスモールオブジェク
トと違うアドレス空間に割り当てられる
• ラージオブジェクト⇒Large Object Heap(LOH)
• スモールオブジェクト⇒Small Object Heap
(SOH)
 現時点では、GCはラージオブジェクトに
対してコンパクションを行わない。
 ラージオブジェクトは割り当て後、すぐに
世代2の一部とみなされる
• 世代2でGCが実行される時じゃないとラージオ
ブジェクトに対してGCは行われない。
 ラージオブジェクトだとメモリの断片化
(フラグメンテーション)が発生します
Large Object Heap
A
B
C
D
Large Object Heap
A
B
C
D
オブジェクトB,Cが到達不能になった。
Large Object Heap
A
D
ガベージコレクション発生後、到達不能の
オブジェクトは解放され、1つの空き容量を作成する
コンパクションは行わない
Large Object Heap
A
E
D
オブジェクトEの割り当て要求に対応するために
作成した空き容量が使用できる
Large Object Heap
このような形で長いこと使っていると、
コンパクションしないので、
LOHだと、メモリの断片化が発生する可能性がある。
→OutOfMemoryExceptionがスルーされる可能性ある
Using System;
Using System.Runtime;
// LOHに対するコンパクションを要求
GCSettings.LOHCompactionMode =
GCLargeObjectHeapCompactionMode.CompactOnce;
// GCが発生してLOHがコンパクションされる
GC.Collect():
LOHもコンパクションできる
そう.NET4.5.1ならね!
 オブジェクトがGCにより回収対象になっ
た後で、オブジェクトのメモリが解放され
る前に何らかのコードを実行できる
class Hoge{
// Finalizeメソッド
~Hode() {
//
}
}
C++のデストラクタと似ていますが、動作は異なる。
C++ではスコープを外れた時に、確実に呼ばれます。
C#ではFinalizeメソッドが実行されるタイミングは全く
制御できません。
マネージヒープ
A
B
B C E
F
ファイナライゼーションリスト
C
D
E
F
Fリーチャブルキュー
Finalizeメソッドが定義してあるオブジェクトを
割り当てる際、型インス箪笥コンストラクターが呼ばれる
前にFinalizationリストにオブジェクトのポインタが配置
される。
マネージヒープ
A
B
B C E
F
ファイナライゼーションリスト
C
D
E
F
Fリーチャブルキュー
C、D、Fが参照されなくなった
マネージヒープ
B
A
B
E
ファイナライゼーションリスト
C
E
C
F
F
Fリーチャブルキュー
ガベージコレクションが実行されると、
FinalizeメソッドのないオブジェクトDは削除され、
Finalizeメソッドのあるオブジェクト、C、FはFリーチャブルキュー
へ参照が移動する
マネージヒープ
B
A
B
E
ファイナライゼーションリスト
C
E
C
F
F
Fリーチャブルキュー
マネージヒープ
B
A
B
C
E
F
E
ファイナライゼーションリスト
Fリーチャブルキュー
Fリーチャブルキューにデータが入ると、
Finalize用のスレッドが動作してキューからデータを
取り出して、Finalizeメソッドを実行します
→ Fリーチャブルキューから参照が消えて、
C,Fオブジェクトは到達不能となる
マネージヒープ
B
A
B
E
E
ファイナライゼーションリスト
Fリーチャブルキュー
その次のタイミングのガベージコレクションで
オブジェクトは削除される
 Finalizeを使うオブジェクトは削除される
までに2回のガベージコレクションが必要
になる。
 CLR
オブジェクトヘッダーの構造
• http://dotnetlogbook.blogspot.jp/2009/09/clr.ht
ml
 大きなオブジェクト
ヒープの秘密
• http://msdn.microsoft.com/ja-jp/magazine/cc53499
 The
Dangers of the Large Object Heap
• https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-
the-large-object-heap/