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/
© Copyright 2024 ExpyDoc