web-dev-qa-db-ja.com

C#でガベージコレクションを強制するためのベストプラクティス

私の経験では、ほとんどの人がガベージコレクションを強制するのは賢明ではないと言うようですが、場合によっては0世代で常に収集されないがメモリが問題である大きなオブジェクトを操作している場合があります強制的に収集しても大丈夫ですか?そうするためのベストプラクティスはありますか?

115
Echostorm

ベストプラクティスは、ガベージコレクションを強制しないことです。

MSDNによると:

「Collectを呼び出すことでガベージコレクションを強制することは可能ですが、ほとんどの場合、パフォーマンスの問題が発生する可能性があるため、これは避ける必要があります。」

ただし、コードを確実にテストして、Collect()の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進みます...

不要になったオブジェクトは必ずクリーンアップしてください。カスタムオブジェクトがある場合は、「usingステートメント」とIDisposableインターフェイスの使用を確認してください。

このリンクには、メモリ/ガベージコレクションの解放などに関する実用的なアドバイスがあります。

http://msdn.Microsoft.com/en-us/library/66x5fx1b.aspx

110
Mark Ingram

ベストプラクティスは、ほとんどの場合、ガベージコレクションを強制しないことです。(私が取り組んだすべてのシステムは、ガベージコレクションを強制しており、解決すると、ガベージコレクションを強制する必要がなくなり、システムが大幅に高速化されます。)

いくつかのケースがありますyouガベージコレクターが行います。これは、マルチユーザーアプリケーション、または一度に複数の要求に応答するサービスでは当てはまりません。

ただし、一部のバッチタイプ処理では、GCの詳細がわかります。例えば。そのアプリケーションを検討してください。

  • コマンドラインでファイル名のリストが与えられます
  • 単一のファイルを処理し、結果を結果ファイルに書き込みます。
  • ファイルの処理中に、ファイルの処理が完了するまで収集できない多くの相互リンクされたオブジェクトを作成します(解析ツリーなど)
  • 処理したファイル間で多くの状態を保持しません

may各ファイルを処理した後、完全なガベージコレクションを強制する必要があることを(慎重に)テストすることができます。

別のケースは、数分ごとに起動していくつかのアイテムを処理するサービスであり、スリープ中は状態を保持しません。次に、スリープに入る直前に完全なコレクションを強制しますmay価値があります。

コレクションを強制することを検討するのは、最近多くのオブジェクトが作成され、現在参照されているオブジェクトが非常に少ないことを知っているときだけです。

GCを自分で強制することなく、このタイプのことについてのヒントを与えることができる場合は、ガベージコレクションAPIが必要です。

Rico Mariani's Performance Tidbits 」も参照してください

31
Ian Ringrose

このように見てください-ゴミ箱が10%になったときに台所のゴミを捨てるのが効率的ですか、それを取り出す前にいっぱいにするのですか?

いっぱいにしないと、外のゴミ箱に出入りする時間が無駄になります。これは、GCスレッドの実行時に起こることと類似しています。実行中はすべてのマネージスレッドが中断されます。誤解がない限り、GCスレッドは複数のAppDomainで共有できるため、ガベージコレクションはすべてのAppDomainに影響します。

もちろん、ごみ箱に何かをすぐに追加しないという状況に遭遇する可能性があります。たとえば、休暇を取る場合などです。その後、外出する前にゴミを捨てることをお勧めします。

このMIGHTは、GCを強制するのに役立ちます。プログラムがアイドル状態の場合、割り当てがないため、使用中のメモリはガベージコレクションされません。

29
Maxam

Rico Mariani で与えられた例は良かったと思います。アプリケーションの状態に大きな変化がある場合、GCをトリガーするのが適切かもしれません。たとえば、ドキュメントエディタでは、ドキュメントが閉じられたときにGCをトリガーしてもかまいません。

20
denis phillips

絶対的なプログラミングの一般的なガイドラインはほとんどありません。半分の時間、誰かが「あなたはそれを間違っている」と言うとき、彼らはただ一定の教義を噴き出しているだけです。 Cでは、自己修正コードやスレッドのようなものを恐れていましたが、GC言語では、GCを強制するか、GCの実行を妨げています。

ほとんどのガイドラインと適切な経験則(および適切な設計慣行)の場合と同様に、確立された基準を回避することが理にかなっている場合はまれです。あなたは、あなたがそのケースを理解していること、あなたのケースが実際に一般的な慣行の廃止を本当に必要としていること、そしてあなたが引き起こしうるリスクと副作用を理解していることを非常に確信しなければなりません。しかし、そのような場合があります。

プログラミングの問題は多種多様であり、柔軟なアプローチが必要です。ガベージコレクションされた言語でGCをブロックすることが理にかなっているケースや、GCが自然に発生するのを待つのではなく、トリガーするのが理にかなっている場所を見てきました。 95%の場合、これらのいずれかが問題に適切に近づいていないことの道しるべとなります。しかし、20回に1回は、おそらく有効なケースがあります。

16
TomB

ガベージコレクションの裏をかこうとしないことを学びました。そうは言っても、ファイルI/Oやデータベース接続などのアンマネージリソースを扱う場合は、usingキーワードを使用するだけです。

13
Kon

私が最近遭遇したGC.Collect()への手動呼び出しが必要なケースは、C#からアクセスされる小さな管理C++オブジェクトにラップされた大きなC++オブジェクトを操作するときでした。

使用される管理メモリの量はごくわずかであるため、ガベージコレクタは呼び出されませんでしたが、使用される管理されていないメモリの量は膨大でした。オブジェクトでDispose()を手動で呼び出すには、オブジェクトが自分で不要になったときを追跡する必要がありますが、GC.Collect()を呼び出すと、参照されなくなったオブジェクトがクリーンアップされます。

9
Morten

ベストプラクティスであるかどうかはわかりませんが、ループ内で大量の画像を操作する(つまり、多数のGraphics/Image/Bitmapオブジェクトを作成および破棄する)ときは、GC.Collectを定期的に使用します。

GCは、プログラムが(ほとんど)アイドル状態であり、集中ループの途中ではないときにのみ実行されるため、手動GCが意味をなす領域のように見えるとどこかで読んだと思います。

9
Michael Stum

既にベストプラクティスを挙げていると思いますが、本当に必要な場合以外は使用しないでください。これらの質問に最初に答えるために、必要に応じてプロファイリングツールを使用して、コードをより詳細に調べることを強くお勧めします。

  1. 必要以上のスコープでアイテムを宣言しているコードが何かありますか
  2. メモリ使用量は本当に高すぎますか
  3. GC.Collect()を使用する前後のパフォーマンスを比較して、本当に役立つかどうかを確認します。
7
Mitchel Sellers

プログラムにメモリリークがなく、オブジェクトが蓄積し、Gen 0でGC処理できないと仮定します。1)長時間参照されているため、Gen1およびGen2にアクセスしてください。 2)これらは大きなオブジェクト(> 80K)なので、LOH(大きなオブジェクトヒープ)に入ります。また、LOHはGen0、Gen1、Gen2のように圧縮を行いません。

「.NETメモリ」のパフォーマンスカウンターを確認すると、1)問題が実際に問題ではないことがわかります。一般に、10 Gen0 GCごとに1 Gen1 GCがトリガーされ、10 Gen1 GCごとに1 Gen2 GCがトリガーされます。理論的には、GC0に圧力がかかっていない場合(プログラムのメモリ使用量が実際に配線されている場合)、GC1とGC2をGCすることはできません。それは私には決して起こりません。

問題2)の場合、「。NETメモリ」パフォーマンスカウンタをチェックして、LOHが肥大化しているかどうかを確認できます。それが本当にあなたの問題の問題であれば、おそらくこのブログが示唆するようにラージオブジェクトプールを作成することができます http://blogs.msdn.com/yunjin/archive/2004/01/27/63642。 aspx

5
Morgan Cheng

ラージオブジェクトは、gen 0ではなくLOH(ラージオブジェクトヒープ)に割り当てられます。gen0でガベージコレクションが行われないと言っているのであれば、そのとおりです。完全なGCサイクル(世代0、1、および2)が発生した場合にのみ収集されると思います。

そうは言っても、大きなオブジェクトを操作していてメモリのプレッシャーが高まっている場合、GCはメモリをより積極的に調整および収集します。

どのような状況で収集するかどうかを言うのは困難です。多数のコントロールなどを含むダイアログウィンドウ/フォームを破棄した後、GC.Collect()を実行していました(フォームとそのコントロールがビジネスオブジェクトの多くのインスタンスを作成するため/多くのデータをロードするためにgen 2になってしまうため)明らかに大きなオブジェクト)が、実際にはそうすることにより、長期的にはプラスまたはマイナスの影響に気付きませんでした。

4
liggett78

それを追加したいと思います:GC.Collect()(+ WaitForPendingFinalizers())の呼び出しは、ストーリーの一部です。他の人が正しく述べているように、GC.COllect()は非決定的なコレクションであり、GC自身(CLR)の裁量に任されています。 WaitForPendingFinalizersへの呼び出しを追加しても、確定的ではない場合があります。このmsdn link からコードを取得し、オブジェクトループの反復を1または2としてコードを実行します。非決定論的な意味がわかります(オブジェクトのデストラクタにブレークポイントを設定します)。正確には、Wait ..()。[Citation reqd。]によって残留オブジェクトが1つ(または2)しかなかった場合、デストラクタは呼び出されません。

コードがアンマネージリソース(外部ファイルハンドルなど)を処理している場合、デストラクタ(またはファイナライザ)を実装する必要があります。

以下に興味深い例を示します。

:MSDNから上記の例を既に試している場合は、次のコードで問題を解決します。

class Program
{    
    static void Main(string[] args)
        {
            SomePublisher publisher = new SomePublisher();

            for (int i = 0; i < 10; i++)
            {
                SomeSubscriber subscriber = new SomeSubscriber(publisher);
                subscriber = null;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine(SomeSubscriber.Count.ToString());


            Console.ReadLine();
        }
    }

    public class SomePublisher
    {
        public event EventHandler SomeEvent;
    }

    public class SomeSubscriber
    {
        public static int Count;

        public SomeSubscriber(SomePublisher publisher)
        {
            publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
        }

        ~SomeSubscriber()
        {
            SomeSubscriber.Count++;
        }

        private void publisher_SomeEvent(object sender, EventArgs e)
        {
            // TODO: something
            string stub = "";
        }
    }

まず、出力がどうなるかを分析してから実行し、次に以下の理由を読むことをお勧めします。

{デストラクタは、プログラムが終了すると暗黙的に呼び出されます。 }オブジェクトを決定論的に消去するには、IDisposableを実装し、Dispose()を明示的に呼び出す必要があります。それが本質です! :)

4
Vaibhav

もう1つ、GC Collectを明示的にトリガーしても、プログラムのパフォーマンスが向上しない場合があります。悪化させる可能性は十分にあります。

.NET GCは、適応するように適切に設計および調整されています。つまり、プログラムのメモリ使用量の「習慣」に従ってGC0/1/2しきい値を調整できます。したがって、しばらく実行するとプログラムに適合します。 GC.Collectを明示的に呼び出すと、しきい値がリセットされます! .NETは、プログラムの「習慣」に再び適応するために時間を費やす必要があります。

私の提案は、常に.NET GCを信頼することです。メモリの問題があれば、「。NETメモリ」パフォーマンスカウンタを確認し、自分のコードを診断します。

2
Morgan Cheng

ベストプラクティスかどうかわからない...

提案:不明な場合は、これを実装しないでください。事実がわかったときに再評価し、パフォーマンステストの前後に実行して検証します。

1
user957965

ただし、コードを確実にテストして、Collect()の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進みます...

私見、これは「あなたのプログラムに将来バグが発生しないことを証明できたら、先に進んでください...」

真剣に、GCを強制することはデバッグ/テストの目的に役立ちます。あなたが他の時にそれをする必要があると思うなら、あなたは間違っているか、プログラムが間違って構築されているかのどちらかです。いずれにせよ、ソリューションはGCを強制していません...

0
Orion Edwards