590 likes | 1.13k Views
.NET Framework における マネージヒープと ガベージコレクション. プログラミング .NET Framework 第 4 版 . 21 章のお話. 実際のオブジェクト. オブジェクトヘッダ 型オブジェクトポインター ( 4byte , 8byte) 型の構造体へのポンタ 同期ブロックインデックス (4byte , 8byte ) ロックとか COM で利用する フィールド. リソースを割り当てる. マネージ ヒープ. NextObjPtr. リソースを割り当てる. マネージ ヒープ. オブジェクト A を割り当てたい!.
E N D
.NET Frameworkにおけるマネージヒープとガベージコレクション
プログラミング.NET Framework 第4版 21章のお話
実際のオブジェクト • オブジェクトヘッダ • 型オブジェクトポインター(4byte, 8byte) • 型の構造体へのポンタ • 同期ブロックインデックス(4byte, 8byte) • ロックとかCOMで利用する • フィールド
リソースを割り当てる マネージヒープ NextObjPtr
リソースを割り当てる マネージヒープ オブジェクトAを割り当てたい! NextObjPtr 同期ブロック 同期ブロックインデックス~フィールドまでが入るようにする 型ハンドル フィールド
リソースを割り当てる マネージヒープ A NextObjPtr オブジェクトAを割り当てたらNextObjPtrjがオブジェクトの直後まで進む
リソースを割り当てる マネージヒープ A B NextObjPtr
リソースを割り当てる マネージヒープ A B C NextObjPtr オブジェクトの割り当ては単なるポインタの加算なので非常に速い
いつからマネージドヒープが無限にあると勘違いしていた?いつからマネージドヒープが無限にあると勘違いしていた?
ガベージコレクション マネージヒープ A B C D E F H G NextObjPtr オブジェクトを割り当てようとしたが十分なアドレス空間がのこっていない! ガベージコレクション 実行!
ガベージコレクションの実行手順 • 全てのスレッドを一時停止 • ガベージコレクション終わるまですべてのスレッドがオブジェクトにアクセスできない • GCマーキングフェイズ • 使ってないオブジェクトを探す • コンパクションフェイズ • 不要なオブジェクトを削除して圧縮する
マーキングフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 参照を表す NextObjPtr クラスの静的またはインスタンスフィールド、メソッドの実引数、ローカル変数などで参照型の変数を総称してルートと呼びます
マーキングフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 0 0 0 0 0 0 0 0 全オブジェクトの同期ブロックインデックスフィールドに含まれているビットに0を指定⇒これはすべてのオブジェクトを 削除するという意味 NextObjPtr
マーキングフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 1 0 1 0 1 0 0 1 ルートから直接参照されているオブジェクトをマークします。 NextObjPtr
マーキングフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 1 0 1 0 1 1 0 1 マークをする際に、もし他のオブジェクトを参照している場合は、そのオブジェクトもマークします。この例だとDをマークするときはGもマークします NextObjPtr
マーキングフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 1 0 1 0 1 1 0 1 これでマーキングフェイズが完了です。 マークされたオブジェクトは到達可能といいます。マークされていないのは到達不能といいます。 NextObjPtr
コンパクションフェイズ ルート: フィールドや 変数 マネージヒープ A B C D E F H G 1 0 1 0 1 1 0 1 マークされているオブジェクトによって使用されているメモリを移動させてメモリ上に連続するようにします。 NextObjPtr
コンパクションフェイズ ルート: フィールドや 変数 マネージヒープ C C A D F G オブジェクトを移動させる際に、オブジェクトを参照しているルートなどは移動した分のバイト数を引く必要があります。
コンパクションフェイズ ルート: フィールドや 変数 マネージヒープ C A F D 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(); }
メソッド内のオブジェクトの生存期間 オブジェクトtは保障される オブジェクトtは保障されない ガベージコレクションが実行されたら消える static void Main(string[] args) { Timer t = new Timer(TimerCallback, null, 0, 2000); // タイミングA Console.ReadLine(); // オブジェクトtの参照 Console.WriteLine(t.ToString()); // タイミングB Console.ReadLine(); }
メソッド内のオブジェクトの生存期間 ReleaseビルドとDebugビルドで動きがかわるぞ! がっでむ! なおDebugでビルドした場合、JITコンパイラが生存期間を恣意的にメソッドの最後まで伸ばします
世代別ガベージコレクタ • CLRのGCは世代別ガレージコレクタを採用している • 世代別GCは次のことを前提にしている • オブジェクトが新しいほど、その生存期間は短い • オブジェクトが古いほど、その生存期間は長い • ヒープの一部分の回収はヒープ全体の回収より高速である
世代別ガベージコレクタ マネージヒープ A B C D E 世代0 新しく追加されるオブジェクトは常に世代0に追加される。
世代別ガベージコレクタ マネージヒープ A B C D E 世代0 しばらくしてオブジェクトCとEが到達不能となるその後、ガベージコレクションが発生したとする。
世代別ガベージコレクタ マネージヒープ A B D 世代1 世代0 ガベージコレクションの後に世代0で生き残ったオブジェクトが世代1に移動して世代0は空になる
世代別ガベージコレクタ マネージヒープ H A B D F G 世代1 世代0 新しいオブジェクトは世代0に割り当てられていく。 世代0の予約サイズを超えた場合にガベージコレクションが実行される。
世代別ガベージコレクタ マネージヒープ A B D H F 世代0 世代1 世代0のオブジェクトのみが検査され、生き残ったオブジェクトは世代1に移動する。世代1は検査していないので、オブジェクトBは生き残る
世代別ガベージコレクタ マネージヒープ L K A B D H F I J 世代0 世代1
世代別ガベージコレクタ マネージヒープ L A B D H F I 世代0 世代1 ガベージコレクションを実行していくと このように世代1が徐々に増加していく。
世代別ガベージコレクタ マネージヒープ M N L A B D H F I O 世代0 世代1 世代1のサイズが上限を超えた時に ガベージコレクション が発生したとしよう
世代別ガベージコレクタ マネージヒープ L A D M O 世代2 世代1 世代0 世代1~世代0のオブジェクトを検査する。世代1の到達可能オブジェクトは世代2となる。 世代0の到達可能オブジェクトは世代1となる
世代別ガベージコレクタ 世代別にGCを行うので、すべてのオブジェクトを検査しなくてすむ。
GCの起動要因 • 世代0の使用量が予約サイズを超えた • 予約サイズはCLRが動的に決める • System.GCをコードで実行 • Windowsが空容量低下の状況を報告
ラージオブジェクト その1 • CLRは個々のオブジェクトをスモールオブジェクトかラージオブジェクトのどちらかであると見なす • 現在85,000バイト以上をラージオブジェクト※ただし、変更される可能性あり • ラージオブジェクトはスモールオブジェクトと違うアドレス空間に割り当てられる • ラージオブジェクト⇒Large Object Heap(LOH) • スモールオブジェクト⇒Small Object Heap (SOH)
ラージオブジェクト その2 • 現時点では、GCはラージオブジェクトに対してコンパクションを行わない。 • ラージオブジェクトは割り当て後、すぐに世代2の一部とみなされる • 世代2でGCが実行される時じゃないとラージオブジェクトに対してGCは行われない。
どういうことだってばよ? ラージオブジェクトだとメモリの断片化(フラグメンテーション)が発生します
LOHの割り当てとGC Large Object Heap B D A C
LOHの割り当てとGC Large Object Heap B D A C オブジェクトB,Cが到達不能になった。
LOHの割り当てとGC Large Object Heap D A ガベージコレクション発生後、到達不能のオブジェクトは解放され、1つの空き容量を作成するコンパクションは行わない
LOHの割り当てとGC Large Object Heap D A E オブジェクトEの割り当て要求に対応するために作成した空き容量が使用できる
LOHの割り当てとGC Large Object Heap このような形で長いこと使っていると、コンパクションしないので、LOHだと、メモリの断片化が発生する可能性がある。 →OutOfMemoryExceptionがスルーされる可能性ある
LOHが例外を生むなら…プロセス再起動するしかないじゃない!LOHが例外を生むなら…プロセス再起動するしかないじゃない!
メソッド内のオブジェクトの生存期間 LOHもコンパクションできるそう.NET4.5.1ならね! Using System; Using System.Runtime; // LOHに対するコンパクションを要求 GCSettings.LOHCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; // GCが発生してLOHがコンパクションされる GC.Collect():
ファイナライゼーションfinalization • オブジェクトがGCにより回収対象になった後で、オブジェクトのメモリが解放される前に何らかのコードを実行できる