.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 G H マネージヒープ NextObjPtr オブジェクトを割り当てようとしたが 十分なアドレス空間がのこっていない! NextObjPtr ガベージコレクション 実行!
ガベージコレクションの実行手順 全てのスレッドを一時停止 GCマーキングフェイズ コンパクションフェイズ ガベージコレクション終わるまですべてのスレッ ドがオブジェクトにアクセスできない GCマーキングフェイズ 使ってないオブジェクトを探す コンパクションフェイズ 不要なオブジェクトを削除して圧縮する
マーキングフェイズ A B C D E F G H マネージヒープ 参照を表す ルート: フィールドや 変数 マネージヒープ A B C D E F G H 参照を表す クラスの静的またはインスタンスフィールド、 メソッドの実引数、ローカル変数などで 参照型の変数を総称してルートと呼びます NextObjPtr
マーキングフェイズ A B C D E F G H マネージヒープ ルート: フィールドや 変数 マネージヒープ A B C D E F G H 全オブジェクトの同期ブロックインデックス フィールドに含まれているビットに0を指定 ⇒これはすべてのオブジェクトを 削除するという意味 NextObjPtr
マーキングフェイズ A B C D E F G H マネージヒープ 1 1 1 1 ルート: フィールドや 変数 マネージヒープ A B C D E F G H 1 1 1 1 ルートから直接参照されている オブジェクトをマークします。 NextObjPtr
マーキングフェイズ A B C D E F G H マネージヒープ 1 1 1 1 1 ルート: フィールドや 変数 マネージヒープ A B C D E F G H 1 1 1 1 1 マークをする際に、もし他のオブジェクトを 参照している場合は、そのオブジェクト もマークします。 この例だとDをマークするときはGもマークします NextObjPtr
マーキングフェイズ A B C D E F G H マネージヒープ 1 1 1 1 1 これでマーキングフェイズが完了です。 ルート: フィールドや 変数 マネージヒープ A B C D E F G H 1 1 1 1 1 これでマーキングフェイズが完了です。 マークされたオブジェクトは到達可能 といいます。 マークされていないのは到達不能といいます。 NextObjPtr
コンパクションフェイズ A B C D E F G H マネージヒープ 1 1 1 1 1 ルート: フィールドや 変数 マネージヒープ A B C D E F G H 1 1 1 1 1 マークされているオブジェクトによって 使用されているメモリを移動させて メモリ上に連続するようにします。 NextObjPtr
コンパクションフェイズ A C C D F G マネージヒープ ルート: フィールドや 変数 マネージヒープ A C C D F G オブジェクトを移動させる際に、 オブジェクトを参照しているルートなどは移動した分のバイト数を引く必要があります。
コンパクションフェイズ A 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 }
メソッド内の オブジェクトの生存期間 static void Main(string[] args) { Timer t = new Timer(TimerCallback, null, 0, 2000); // タイミングA Console.ReadLine(); // オブジェクトtの参照 Console.WriteLine(t.ToString()); // タイミングB } オブジェクト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 D マネージヒープ 世代1 世代0 ガベージコレクションの後に世代0で生き残った オブジェクトが世代1に移動して世代0は空になる
世代別ガベージコレクタ A B D F G H マネージヒープ 世代1 世代0 新しいオブジェクトは世代0に割り当てられていく。 世代0の予約サイズを超えた場合に ガベージコレクションが実行される。
世代別ガベージコレクタ A B D F H マネージヒープ 世代1 世代0
世代別ガベージコレクタ マネージヒープ A B D F H I J K L 世代1 世代0
世代別ガベージコレクタ A B D F H I L マネージヒープ 世代0 世代1 ガベージコレクションを実行していくと このように世代1が徐々に増加していく。
世代別ガベージコレクタ A B D F H I L M N O マネージヒープ 世代0 世代1 世代1のサイズが上限を超えた時に ガベージコレクション が発生したとしよう
世代別ガベージコレクタ A D L M O マネージヒープ 世代2 世代1 世代0 世代1~世代0のオブジェクトを検査する。 世代1の到達可能オブジェクトは世代2となる。 世代0の到達可能オブジェクトは世代1となる
世代別ガベージコレクタ 世代別にGCを行うので、すべてのオブジェクトを検査しなくてすむ。
GCの起動要因 世代0の使用量が予約サイズを超えた System.GCをコードで実行 Windowsが空容量低下の状況を報告 予約サイズはCLRが動的に決める System.GCをコードで実行 Windowsが空容量低下の状況を報告
ラージオブジェクト その1 CLRは個々のオブジェクトをスモールオブジェクトかラージオブジェクトのどちらかであると見なす ラージオブジェクト その1 CLRは個々のオブジェクトをスモールオブジェクトかラージオブジェクトのどちらかであると見なす 現在85,000バイト以上をラージオブジェクト ※ただし、変更される可能性あり ラージオブジェクトはスモールオブジェクトと違うアドレス空間に割り当てられる ラージオブジェクト⇒Large Object Heap(LOH) スモールオブジェクト⇒Small Object Heap (SOH)
ラージオブジェクト その2 現時点では、GCはラージオブジェクトに対してコンパクションを行わない。 ラージオブジェクト その2 現時点では、GCはラージオブジェクトに対してコンパクションを行わない。 ラージオブジェクトは割り当て後、すぐに世代2の一部とみなされる 世代2でGCが実行される時じゃないとラージオ ブジェクトに対してGCは行われない。
どういうことだってばよ?
どういうことだってばよ? ラージオブジェクトだとメモリの断片化(フラグメンテーション)が発生します
LOHの割り当てとGC Large Object Heap A B C D
LOHの割り当てとGC Large Object Heap A B C D オブジェクトB,Cが到達不能になった。
LOHの割り当てとGC A D Large Object Heap ガベージコレクション発生後、到達不能の オブジェクトは解放され、1つの空き容量を作成する コンパクションは行わない
LOHの割り当てとGC A E D Large Object Heap
LOHの割り当てとGC Large Object Heap →OutOfMemoryExceptionがスルーされる可能性ある
LOHが例外を生むなら… プロセス再起動するしかないじゃない!
.NET 4.5.1 なら その必要はないわ!
メソッド内の オブジェクトの生存期間 LOHもコンパクションできる そう.NET4.5.1ならね! Using System; Using System.Runtime; // LOHに対するコンパクションを要求 GCSettings.LOHCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; // GCが発生してLOHがコンパクションされる GC.Collect(): LOHもコンパクションできる そう.NET4.5.1ならね!
ファイナライゼーション finalization オブジェクトがGCにより回収対象になった後で、オブジェクトのメモリが解放される前に何らかのコードを実行できる
Finalizeメソッド class Hoge{ // Finalizeメソッド ~Hode() { // } } C++のデストラクタと似ていますが、動作は異なる。 C++ではスコープを外れた時に、確実に呼ばれます。 C#ではFinalizeメソッドが実行されるタイミングは全く 制御できません。
ファイナライゼーションの動作 A B C D E F B C E F マネージヒープ ファイナライゼーションリスト Fリーチャブルキュー Finalizeメソッドが定義してあるオブジェクトを 割り当てる際、型インス箪笥コンストラクターが呼ばれる 前にFinalizationリストにオブジェクトのポインタが配置 される。
ファイナライゼーションの動作 A B C D E F B C E F マネージヒープ ファイナライゼーションリスト Fリーチャブルキュー C、D、Fが参照されなくなった
ファイナライゼーションの動作 A B C E F B E C F マネージヒープ ファイナライゼーションリスト Fリーチャブルキュー ガベージコレクションが実行されると、 FinalizeメソッドのないオブジェクトDは削除され、 Finalizeメソッドのあるオブジェクト、C、FはFリーチャブルキュー へ参照が移動する
ファイナライゼーションの動作 マネージヒープ A B C E F B E C F ファイナライゼーションリスト Fリーチャブルキュー
ファイナライゼーションの動作 A B C E F B E マネージヒープ ファイナライゼーションリスト Fリーチャブルキュー Fリーチャブルキューにデータが入ると、 Finalize用のスレッドが動作してキューからデータを 取り出して、Finalizeメソッドを実行します → Fリーチャブルキューから参照が消えて、 C,Fオブジェクトは到達不能となる
ファイナライゼーションの動作 A B E B E マネージヒープ ファイナライゼーションリスト Fリーチャブルキュー その次のタイミングのガベージコレクションで オブジェクトは削除される
ファイナライゼーション finalization 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/