C#のメモリ管理

.NETメモリリソースでは、に分けることができマネージリソースアンマネージリソースCLRリソース管理により管理、アンマネージリソースではありません。

 

1、管理対象リソース

管理対象リソースは、「スタック」とに格納されている「マネージヒープ。」

 そして「参照型のインスタンス「値型としてローカル変数の例」の参照は、」スタックに格納されます。

「クラスのメンバとしての値の種類の例」と「参照タイプのインスタンスは、」ヒープに格納されます。

 

1.1スタック

また、スレッドのスタック領域として知られているスタックが比較的小さい場合、後者は構造の外に前進され、メモリが低い高アドレスによって充填されています。

可変スタックポインタが高いアドレスに戻される変数、モバイル下位のアドレスへのポインタが、あるスタックの次の利用可能なスペース、スタックのスタックポインタ。

パブリック 静的 ボイドのmain()
{     
    int型のn = 1 チャー C = ' A ' 
}

上記のコード例では、2000年開始からのアドレスと仮定する。コードが4バイト、ポインターダウン4バイト、変数の値が整数であるN 2000にアドレス1997記憶されているため、メイン、変数nを入力して最初に開始。

コードの次の行は、文字変数cはダウン空間の2バイト、2つのバイトのポインタ、変数c「」格納されたアドレス1996から1995までの値を割り当てられます。

主な機能の右側に括弧内の最後の実行は、実行が終了し、値numとCを削除します。、削除、移動した後、4バイトのポインタを2バイト、Cの値を移動するには、NUMの値が削除されました。

スタックメモリは、連続した、とだけからまたはスタックの最上位に来ているので、GCのための必要はありません。

 

 1.2、ヒープ

 ヒープは、それがCLRによって管理され、ヒープ短い管理されています。これは、プログラムは、メモリのCLRアプリケーションを実行しているされ、メモリ空間は、プロセスの一部が所属です。

このスペースはに分けることができます。

1. GCヒープ:0,1,2は、代わって3つの領域に分割された大きなオブジェクトヒープ(ラージオブジェクトヒープ)がスタック2つの世代の一部である、より大きなスペースの発生が大きくなります。

2.いくつかのアプリケーションドメイン:上部がそれぞれ有するローディング・ヒープ(ヒープローダー)は、のヒープメモリにロードされたメソッドテーブル(方法、表)、静的フィールドは、インタフェースを達成するために、その他すべての機能、基板の種類を、その上に格納されました。GCヒープ負荷ドメインをアンインストールするプログラムを作成するには、そのライフサイクルに影響を与えず。

 回復メカニズムの3つの世代のGC:新しく作成されたオブジェクトが世代0で、世代0がいっぱいになると、リサイクルが第一世代になることはありません、ガベージコレクションがトリガされます。世代が回復したときにそう、彼は第二世代になることを残しました。

 ラージ・オブジェクト・ヒープ:85キロバイトのインスタンスよりも、大きなオブジェクトヒープに格納されます。

 次のようにスタックレイアウトの例です。

 

シンクブロックインデックス(索引ブロック同期):シンクブロックインデックスは、また、位置-4 0バイト(例えば32ビットタイプ)にオブジェクトのアドレス、オブジェクトヘッダポインタと呼ばれます。

オブジェクトを判断するために使用されるインデックスシンクブロック同期スレッドが使用されるか、または未使用されます。

値がアイドル表す場合、同期ブロックインデックスの値をチェックし、オブジェクト上で動作するスレッドがある場合、動作します。

次に、新しいCLRシンクブロックに配列増大シンクブロックは、インデックス値が占有表し、このブロックのシンクブロックのインデックス値、に書き込まれます。

スレッドが消耗し、彼は未使用の価値を表す指標値に戻りました。

 

メソッドテーブルポインタ:メソッドテーブル型オブジェクトへのポインタ。

オブジェクトのタイプは、作成したヒープにロードされたアセンブリをロードしています。

オブジェクトストアタイプ静的フィールドおよびメソッドテーブルは、そのタイプのすべてのインスタンスによって共有されます。

メソッドテーブルは、静的および非静的関数の機能を含むすべての機能を含みます。

 

 

2、アンマネージリソース

非托管资源是不受CLR管理的资源,最常见的非托管资源是包装操作系统资源的对象,例如文件、网络连接、数据库连接等。

C#提供Object.Finalize函数在GC时调用来释放这类资源,这个函数默认是空的,不过Object.Finalize是无法重载的,它是根据类的析构函数自动生成的,因此要将释放非托管资源的代码写在析构函数里。

 

说下GC调用Finalize的流程:

new一个包含Finalize函数的对象,开辟内存后,指向它的指针会被存放到终结列表Finalization List

垃圾回收时,被当作垃圾的对象如果同时存在于终结列表里,就会将该对象从终结列表中移除,并存入终结可达列表(F-reachable List)。并且这些对象变为可达,不会被GC回收,即表示这些对象提升了一代。

该队列中的对象都是可达的,并需要执行Finalize函数。执行Finalize函数是由一个高优先级的CLR线程进行的,执行完毕后,会将对象的指针从Freachable Queue中移除。

当再次进行垃圾回收时,原终结可达列表里的对象经过处理都变为不可达对象,只有当这一代内存不足时才会对对象进行垃圾回收,这些对象内存才会真正释放掉。所以含有Finalize函数的对象最少要经过两次垃圾回收才会被真正释放。

 

因为非托管资源比较有限,等GC调用Finalize释放,会让非托管资源处于不必要的空闲状态,影响性能。因此定义了一个IDispose接口,用于手动释放非托管资源。

在一个包含非托管资源的类中,关于资源释放的标准做法是:

① 继承IDisposable接口;

② 实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);

③ 实现类析构函数,在其中释放非托管资源。

 

提一下,继承了IDispose接口的类可以使用using,当超出作用域后,系统会自动调用Dispose函数。

 

おすすめ

転載: www.cnblogs.com/pj2933/p/10927014.html