web-dev-qa-db-ja.com

C#でのガベージコレクションは実行されません。どうして?

ガベージコレクターの機能を確認するために、簡単な実験を試みました。 。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;
    }

そしてここに結果があります:

メモリ割り当て前

http://i.stack.imgur.com/ZhUKc.png

メモリ割り当て後

http ://i.stack.imgur.com/NyXJ6.png

関数スコープを離れる前に

http ://i.stack.imgur.com/MnTrm.png

関数スコープを離れた後

http ://i.stack.imgur.com/MiA6T.png

ガベージコレクターが、nullに設定された後でも、配列a、b、c、dによって割り当てられたメモリの割り当てを解除しなかったのはなぜですか?

24
Xegara

.NETガベージコレクターは、高度に最適化された複雑なソフトウェアの獣です。プログラムをできるだけ速く実行するように最適化されており、その際にメモリが多すぎないを使用します。

メモリを解放するプロセスには時間がかかるため、ガベージコレクタは、プログラムがメモリを使用するまで、メモリの実行を待機することがよくあります全体。次に、すべての作業を一度に実行します。これにより、比較的長い時間が経過すると、プログラムの遅延が小さくなります(以前の多くの小さな遅延の代わりに、プログラムの速度が低下します)。

これはすべて、ガベージコレクターの実行時間が予測不可能であることを意味します。

テストを数回(ループ内にSleep()を使用して)呼び出し、メモリ使用量がゆっくりと増加するのを見ることができます。プログラムが使用可能な物理メモリのかなりの部分を消費し始めると、そのメモリ使用量は突然ゼロ近くまで低下します。

いくつかのレベルのガベージコレクションを強制する関数(GC.Collect()など)がいくつかありますが、それはそれらを使用しないことを強くお勧めします何をしているのかわからない限り、これは傾向があるためですソフトウェアを遅くし、ガベージコレクターが最適な方法で作業を行うのを停止します。

66
DrKoch

内部でメモリの割り当てを解除した場合でも、オペレーティングシステムにメモリを戻す義務はありません。将来、より多くのメモリが要求されると想定し、ページをリサイクルします。オペレーティングシステムの番号は、プログラムが要求したメモリを使用するためにどのように選択したかについては何も知りません。

実際にメモリを明示的に要求して解放したい場合は、Pinvokeの安全でないコードを介してVirtualAlloc()を呼び出す必要があります。

24
pjc50

ガベージコレクションは高価です。できるだけめったに実行したくないだけです。理想的には決して。したがって、システムは、基本的にメモリが不足するまで、ガベージコレクションを可能な限り遅らせようとします。

メモリの割り当てにはコストがかかります。ランタイムがメモリを割り当てた後は、プログラムのランタイムの1回の間にその量のメモリが必要になった場合、将来のある時点で同様の量のメモリがあり、メモリを再度割り当てる必要がないようにしたいと考えています。

したがって、テスト中にifガベージコレクションが発生した場合でも、タスクマネージャーまたはProcess Explorerには表示されません。 [ 〜#〜] clr [〜#〜] とにかくそれを解放しません。

あなたが説明しているものは、参照カウントガベージコレクターと呼ばれます。ただし、CLI VESの現在存在するすべての実装は、トレースGCを使用します。トレースGCは参照をカウントしません。それらをトレースします実行中のみ。トレースGCは、オブジェクトがまだ到達可能かどうかを認識しません(== --- ==)まで実際にオブジェクトグラフをトレースし、オブジェクトグラフをトレースするのはコレクションを実行する必要があります。つまり、メモリが不足した場合です。

5
Jörg W Mittag

一部の情報は、リンク先の記事にすでに含まれています。観察した動作が正しいことを示すいくつかの兆候があります。

...ガベージコレクターは、オブジェクトを使用されなくなったものとして扱う場合があります(必須ではありません)。

...後で不特定の時点で.。

GC.Collect()

少なくとも古い(非同時)バージョンのガベージコレクターにとって重要なことの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のみのメモリであるワーキングセットが表示されるためです。ただし、一部のメモリはにスワップされている可能性があります。ディスク、したがって、タスクマネージャによって報告されない場合があります。

5
Thomas Weller

[〜#〜] clr [〜#〜] は、システムリソースを消費するため、メモリリリースごとにガベージコレクタを実行するわけではありません。したがって、ガベージコレクタは、増大するメモリサイズに基づいて定期的に呼び出されます。参照されていないすべてのメモリリークがクリアされます。

また、ガベージコレクターは、GC.Collect()メソッドを使用して明示的に呼び出すことができますが、明示的に使用することはお勧めしません。

5
svasuvelu