回答:
ベストプラクティスは、ガベージコレクションを強制しないことです。
MSDNによると:
「収集を呼び出すことでガベージコレクションを強制することは可能ですが、ほとんどの場合、パフォーマンスの問題が発生する可能性があるため、これを回避する必要があります。」
ただし、コードを確実にテストして、Collect()の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進んでください...
不要になったオブジェクトがクリーンアップされていることを確認してください。カスタムオブジェクトがある場合は、「usingステートメント」とIDisposableインターフェイスの使用を見てください。
このリンクには、メモリ/ガベージコレクションなどの解放に関するいくつかの実用的なアドバイスがあります。
TargetOfInvocationNullException
ます。
このように見てください。ゴミ箱が10%になったときに台所のゴミを捨てるか、ゴミを出す前にいっぱいにしておく方が効率的ですか。
いっぱいにならないようにすることで、外のゴミ箱に出入りする時間を無駄にすることになります。これは、GCスレッドの実行時に何が起こるかに似ています。すべての管理対象スレッドは、実行中に中断されます。そして、私が間違っていなければ、GCスレッドは複数のAppDomain間で共有される可能性があるため、ガベージコレクションはそれらすべてに影響します。
もちろん、すぐにゴミ箱に何も追加しないという状況に遭遇するかもしれません-たとえば、休暇を取るつもりなら。その後、出かける前にゴミを捨てるのがいいでしょう。
これは、GCを強制することが役立つ可能性がある1回になる可能性があります。プログラムがアイドル状態の場合、割り当てがないため、使用中のメモリはガベージコレクションされません。
ベストプラクティスは、ほとんどの場合、ガベージコレクションを強制しないことです。 (私が取り組んだすべてのシステムは、ガベージコレクションを強制し、解決されればガベージコレクションを強制する必要がなくなり、システムを大幅に高速化するという問題を強調していました。)
ありますいくつかのケースあなたはその後、ガベージコレクタが行うメモリ使用量の詳細を知っているが。これは、マルチユーザーアプリケーション、または一度に複数の要求に応答するサービスでは当てはまりません。
ただし、一部のバッチタイプの処理では、GCよりもよく知っています。たとえば、そのアプリケーションを考えてみましょう。
あなたはありますが、それぞれのファイルを処理した後で、完全なガベージコレクションを強制する必要があることをテストする(気をつけた後)のケースを作ることができます。
もう1つのケースは、数分ごとに起動していくつかのアイテムを処理し、スリープ状態の間は状態を維持しないサービスです。それからちょうど寝る前にフルコレクションを強制するかもしれませんなるは価値があります。
コレクションの強制を検討するのは、最近多くのオブジェクトが作成され、現在参照されているオブジェクトがほとんどないことを知っているときだけです。
GCに自分自身を強制する必要なしに、このタイプのことについてのヒントを与えることができる場合は、ガベージコレクションAPIを使用したいと思います。
「Rico MarianiのパフォーマンスTidbits」も参照してください。
Rico Marianiの例は良かったと思います。アプリケーションの状態に大きな変化がある場合は、GCをトリガーするのが適切な場合があります。たとえば、ドキュメントエディターでは、ドキュメントが閉じられたときにGCをトリガーしても問題ありません。
絶対的なプログラミングの一般的なガイドラインはほとんどありません。半分の時間、誰かが「あなたはそれを間違っている」と言うとき、彼らはある量の教義を噴出しているだけです。Cでは、自己変更コードやスレッドなどを恐れていましたが、GC言語では、GCを強制するか、GCの実行を妨げています。
ほとんどのガイドラインと優れた経験則(および優れた設計手法)の場合と同様に、確立された基準を回避することが理にかなっていることはまれです。あなたは、あなたがその事件を理解していること、あなたの事件が本当に一般的な慣行の廃止を必要としていること、そしてあなたが引き起こし得るリスクと副作用を理解していることを確信する必要があります。しかし、そのような場合があります。
プログラミングの問題はさまざまで、柔軟なアプローチが必要です。ガベージコレクションされた言語でGCをブロックすることが理にかなっているケースや、自然に発生するのを待つのではなくトリガーすることが理にかなっている場所を見てきました。95%の確率で、これらのどちらかが問題の権利に近づかなかったという標識になります。しかし、20回に1回は、おそらくそれを正当化する正当なケースがあります。
ガベージコレクションの裏をかこうとしないことを学びました。そうは言っても、using
ファイルI / Oやデータベース接続などのアンマネージリソースを処理するときは、キーワードを使用することに固執します。
それがベストプラクティスであるかどうかはわかりませんが、大量の画像をループで処理する場合(つまり、多くのGraphics / Image / Bitmapオブジェクトを作成して破棄する場合)、定期的にGC.Collectを使用します。
私がどこかで読んだのは、プログラムが(ほとんど)アイドル状態で、集中ループの途中ではないときにGCが実行されるため、手動のGCが理にかなっている領域のように見えることです。
手動での呼び出しを必要とする最近遭遇した1つのケースはGC.Collect()
、小さなマネージC ++オブジェクトにラップされた大きなC ++オブジェクトを使用していて、C#からアクセスされる場合でした。
使用されるマネージメモリの量はごくわずかであるため、ガベージコレクタは呼び出されませんでしたが、使用されるアンマネージメモリの量は膨大でした。Dispose()
オブジェクトを手動で呼び出すと、オブジェクトが不要になったときに自分で追跡する必要がありますが、呼び出しをGC.Collect()
行うと、参照されなくなったオブジェクトはすべてクリーンアップされます。
GC.AddMemoryPressure (ApproximateSizeOfUnmanagedResource)
は、コンストラクタで呼び出し、後でGC.RemoveMemoryPressure(addedSize)
ファイナライザで呼び出すことです。このようにして、収集できるアンマネージ構造のサイズを考慮して、ガベージコレクターが自動的に実行されます。stackoverflow.com/questions/1149181/...
プログラムにメモリリークがなく、オブジェクトが蓄積され、Gen 0でGCできないと仮定します。理由は次のとおりです。1)それらは長期間参照されるため、Gen1とGen2に入る。2)それらは大きなオブジェクト(> 80K)なので、LOH(Large Object Heap)に入ります。また、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に示されているように、ラージオブジェクトプールを作成できます。
ラージオブジェクトは、gen 0ではなくLOH(ラージオブジェクトヒープ)に割り当てられます。gen0でガベージコレクションされないという場合は、そのとおりです。完全なGCサイクル(世代0、1、2)が発生したときにのみ収集されると思います。
そうは言っても、大きなオブジェクトを操作していてメモリのプレッシャーが高まっている場合、GCはメモリをより積極的に調整および収集することになると思います。
収集するかどうか、どのような状況かはわかりにくい。多数のコントロールなどを含むダイアログウィンドウ/フォームを破棄した後、GC.Collect()を使用していました(フォームとそのコントロールが第2世代になるまでには、ビジネスオブジェクトの多数のインスタンスを作成する/大量のデータをロードするためです-いいえ大きなオブジェクトは明らかに)、しかし実際にそうすることによって長期的にはプラスまたはマイナスの影響に気づかなかった。
追加したいのは、GC.Collect()(+ WaitForPendingFinalizers())の呼び出しはストーリーの一部です。他の人が正しく述べているように、GC.COllect()は非決定的なコレクションであり、GC自体(CLR)の裁量に任されています。WaitForPendingFinalizersの呼び出しを追加しても、確定的ではない場合があります。このmsdn リンクからコードを取得し、オブジェクトループの反復を1または2としてコードを実行します。非決定論的な意味がわかります(オブジェクトのデストラクタにブレークポイントを設定します)。正確には、Wait ..()によって残留オブジェクトが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()を明示的に呼び出す必要があります。それが本質です!:)
さらに、GC Collectを明示的にトリガーしても、プログラムのパフォーマンスが向上しない場合があります。それを悪化させることはかなり可能です。
.NET GCは適切に設計され、適応できるように調整されています。つまり、プログラムのメモリ使用量の「習慣」に応じてGC0 / 1/2しきい値を調整できます。したがって、しばらく実行すると、プログラムに適合します。GC.Collectを明示的に呼び出すと、しきい値がリセットされます!そして.NETは、プログラムの「習慣」に再び適応するために時間を費やす必要があります。
私の提案は常に.NET GCを信頼することです。メモリの問題が発生した場合は、「。NETメモリ」パフォーマンスカウンターを確認し、自分のコードを診断します。
それがベストプラクティスかどうかわからない...
提案:不明な場合は、これを実装しないでください。事実が判明したときに再評価し、パフォーマンステストの前後を実行して確認します。
ただし、コードを確実にテストして、Collect()の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進んでください...
私見、これは「プログラムに将来バグがないことを証明できたら、先に進んでください...」と同じです。
真剣に考えると、GCの強制はデバッグやテストの目的で役立ちます。それ以外のときに実行する必要があると思われる場合は、誤っているか、プログラムが正しく構築されていません。いずれにせよ、ソリューションはGCを強制していません...