ガベージコレクターの機能を確認するために、簡単な実験を試みました。 。NET の自動メモリ管理について.9自動メモリ管理(MSDN)を参照します。私には、C++で同等の共有ポインターのように聞こえました。オブジェクトの参照カウンターがゼロになると、ガベージコレクターによって割り当てが解除されます。
そこで、メインフォーム内に関数を作成してみました。この関数は、コンストラクターの後に実行されるメインフォームのShownイベント関数内で呼び出されました。これが実験的なコードです。
public void experiment()
{
int[] a = new int[100000];
int[] b = new int[100000];
int[] c = new int[100000];
int[] d = new int[100000];
a = null;
b = null;
c = null;
d = null;
}
そしてここに結果があります:
ガベージコレクターが、nullに設定された後でも、配列a、b、c、dによって割り当てられたメモリの割り当てを解除しなかったのはなぜですか?
.NETガベージコレクターは、高度に最適化された複雑なソフトウェアの獣です。プログラムをできるだけ速く実行するように最適化されており、その際にメモリが多すぎないを使用します。
メモリを解放するプロセスには時間がかかるため、ガベージコレクタは、プログラムがメモリを使用するまで、メモリの実行を待機することがよくあります全体。次に、すべての作業を一度に実行します。これにより、比較的長い時間が経過すると、プログラムの遅延が小さくなります(以前の多くの小さな遅延の代わりに、プログラムの速度が低下します)。
これはすべて、ガベージコレクターの実行時間が予測不可能であることを意味します。
テストを数回(ループ内にSleep()を使用して)呼び出し、メモリ使用量がゆっくりと増加するのを見ることができます。プログラムが使用可能な物理メモリのかなりの部分を消費し始めると、そのメモリ使用量は突然ゼロ近くまで低下します。
いくつかのレベルのガベージコレクションを強制する関数(GC.Collect()
など)がいくつかありますが、それはそれらを使用しないことを強くお勧めします何をしているのかわからない限り、これは傾向があるためですソフトウェアを遅くし、ガベージコレクターが最適な方法で作業を行うのを停止します。
内部でメモリの割り当てを解除した場合でも、オペレーティングシステムにメモリを戻す義務はありません。将来、より多くのメモリが要求されると想定し、ページをリサイクルします。オペレーティングシステムの番号は、プログラムが要求したメモリを使用するためにどのように選択したかについては何も知りません。
実際にメモリを明示的に要求して解放したい場合は、Pinvokeの安全でないコードを介してVirtualAlloc()を呼び出す必要があります。
ガベージコレクションは高価です。できるだけめったに実行したくないだけです。理想的には決して。したがって、システムは、基本的にメモリが不足するまで、ガベージコレクションを可能な限り遅らせようとします。
メモリの割り当てにはコストがかかります。ランタイムがメモリを割り当てた後は、プログラムのランタイムの1回の間にその量のメモリが必要になった場合、将来のある時点で同様の量のメモリがあり、メモリを再度割り当てる必要がないようにしたいと考えています。
したがって、テスト中にifガベージコレクションが発生した場合でも、タスクマネージャーまたはProcess Explorerには表示されません。 [ 〜#〜] clr [〜#〜] とにかくそれを解放しません。
あなたが説明しているものは、参照カウントガベージコレクターと呼ばれます。ただし、CLI VESの現在存在するすべての実装は、トレースGCを使用します。トレースGCは参照をカウントしません。それらをトレースします実行中のみ。トレースGCは、オブジェクトがまだ到達可能かどうかを認識しません(== --- ==)まで実際にオブジェクトグラフをトレースし、オブジェクトグラフをトレースするのはコレクションを実行する必要があります。つまり、メモリが不足した場合です。
一部の情報は、リンク先の記事にすでに含まれています。観察した動作が正しいことを示すいくつかの兆候があります。
...ガベージコレクターは、オブジェクトを使用されなくなったものとして扱う場合があります(必須ではありません)。
...後で不特定の時点で.。
少なくとも古い(非同時)バージョンのガベージコレクターにとって重要なことの1つは、ガベージコレクターが別のスレッドで実行されることです。デバッガーで次のことを確認できます。
_0:003> !threads
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 1b08 0058f218 a020 Enabled 025553ac:02555fe8 0058b868 1 MTA
2 2 1e9c 005a78c8 b220 Enabled 00000000:00000000 0058b868 0 MTA (Finalizer)
_
ファイナライザスレッドはガベージコレクションを実行します。他のすべてのスレッドは操作中に中断されるため、再編成中にスレッドがオブジェクトを変更することはできません。
しかし、なぜそれが重要なのでしょうか。
シナリオでも、ガベージコレクションを実行するためにGC.Collect()
を呼び出した場合でも、ガベージコレクションがすぐに適用されない理由を説明します。ガベージコレクターを実行するには、スレッドスイッチも必要です。したがって、非同時ガベージコレクションに必要な最小コードは次のとおりです。
_GC.Collect();
Thread.Sleep(0);
_
メモリ管理が心配な場合は、 IDisposableに関するすばらしい回答 も確認してください。
また、タスクマネージャでメモリ消費量を確認することは信頼できないと誰もまだ説明していません。
.NETは仮想メモリに直接作用します。つまり、仮想メモリマネージャを使用します。ヒープ、つまりヒープマネージャーは使用しません。代わりに、マネージヒープと呼ばれる独自のメモリ管理を使用します。
.NETは、Windows(カーネル)からメモリを取得します。内部に.NETオブジェクトがないWindowsから新しいメモリを取得するとします。 Windowsの観点からは、メモリはなくなっています(.NETに与えられています)。ただし、.NETの観点からは、無料であり、オブジェクトで使用できます。
繰り返しますが、デバッガーで次のことを確認できます。
_0:003> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 60 71cb9000 ( 1.778 Gb) 88.91%
<unknown> 84 986f000 ( 152.434 Mb) 67.09% 7.44%
Image 189 2970000 ( 41.438 Mb) 18.24% 2.02%
...
_
_<unknown>
_として報告されるのは、Windowsの観点から見た仮想メモリです。この場合、150MBが使用されます。
_0:003>!dumpheap -stat
...
00672208 32 8572000 Free
...
_
したがって、8.5 MBは.NETの観点からは無料ですが、(まだ)Windowsに戻されておらず、そこで使用されていると報告されていることがわかります。
タスクマネージャのデフォルトの列設定を変更していない場合は、さらに悪いことになります。これは、RAMのみのメモリであるワーキングセットが表示されるためです。ただし、一部のメモリはにスワップされている可能性があります。ディスク、したがって、タスクマネージャによって報告されない場合があります。
[〜#〜] clr [〜#〜] は、システムリソースを消費するため、メモリリリースごとにガベージコレクタを実行するわけではありません。したがって、ガベージコレクタは、増大するメモリサイズに基づいて定期的に呼び出されます。参照されていないすべてのメモリリークがクリアされます。
また、ガベージコレクターは、GC.Collect()メソッドを使用して明示的に呼び出すことができますが、明示的に使用することはお勧めしません。