私の経験では、ガベージ コレクションを強制するのは賢明ではないとほとんどの人が言うようですが、場合によっては、常に 0 世代で収集されるわけではなく、メモリが問題になる大きなオブジェクトを操作している場合があります。強制的に収集してもよろしいですか?そうするためのベストプラクティスはありますか?
16 に答える
ベスト プラクティスは、ガベージ コレクションを強制しないことです。
MSDNによると:
「Collect を呼び出してガベージ コレクションを強制することは可能ですが、ほとんどの場合、これはパフォーマンスの問題を引き起こす可能性があるため、避ける必要があります。」
ただし、コードを確実にテストして、 Collect() の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進んでください...
オブジェクトが不要になったら、オブジェクトがクリーンアップされるようにしてください。カスタム オブジェクトがある場合は、"using ステートメント" と IDisposable インターフェイスの使用を検討してください。
このリンクには、メモリの解放やガベージ コレクションなどに関する実用的なアドバイスがあります。
こう考えると、生ごみは10%になったら捨てるのと、満タンにしてから出すのとどちらが効率的ですか?
満タンにしないと、外のゴミ箱に出入りする時間を無駄にします。これは、GC スレッドの実行時に発生することと似ています。実行中はすべてのマネージド スレッドが中断されます。私が間違っていなければ、GC スレッドは複数の AppDomain 間で共有できるため、ガベージ コレクションはそれらすべてに影響します。
もちろん、すぐにゴミ箱に何も追加しないという状況に遭遇する可能性があります。たとえば、休暇を取る場合などです。それなら、出かける前にゴミを捨てるのがいいでしょう。
これは、GC を強制することが役立つ場合があります。プログラムがアイドル状態の場合、割り当てがないため、使用中のメモリはガベージ コレクションされません。
ベスト プラクティスは、ほとんどの場合、ガベージ コレクションを強制しないことです。 (私が取り組んだすべてのシステムで、強制的にガベージ コレクションが行われ、解決されればガベージ コレクションを強制的に実行する必要がなくなり、システムの速度が大幅に向上するという問題がありました。)
ガベージ コレクターよりもメモリ使用量について詳しい場合がいくつかあります。これは、マルチ ユーザー アプリケーションや、一度に複数の要求に応答するサービスでは当てはまりません。
ただし、一部のバッチタイプの処理では、GC よりも多くのことを知っています。たとえば、次のようなアプリケーションを考えてみましょう。
- コマンドラインでファイル名のリストが与えられます
- 単一のファイルを処理し、結果を結果ファイルに書き込みます。
- ファイルの処理中に、ファイルの処理が完了するまで収集できない相互リンクされたオブジェクトが多数作成されます (解析ツリーなど)。
- 処理したファイル間で多くの状態を保持しません。
各ファイルを処理した後に完全なガベージ コレクションを強制する必要があることを (慎重に) テストした後、ケースを作成できる場合があり ます。
もう 1 つのケースは、いくつかのアイテムを処理するために数分ごとにウェイクアップし、スリープ中は状態を保持しないサービスです。次に、スリープする直前に完全なコレクションを強制することは価値があるかもしれません.
コレクションを強制することを検討するのは、最近多くのオブジェクトが作成され、現在参照されているオブジェクトがほとんどないことがわかっている場合だけです。
自分でGCを強制することなく、この種のことについてヒントを与えることができる場合は、ガベージコレクションAPIが必要です。
「リコ・マリアーニのパフォーマンスのヒント」も参照してください。
Rico Marianiの例は良かったと思います。アプリケーションの状態に大きな変化があった場合、GC をトリガーするのが適切かもしれません。たとえば、ドキュメント エディターでは、ドキュメントを閉じたときに GC をトリガーしても問題ない場合があります。
プログラミングには、絶対的な一般的なガイドラインはほとんどありません。半分の確率で、誰かが「あなたのやり方は間違っている」と言うとき、彼らはある程度のドグマを吐き出しているだけです。C では、自己変更コードやスレッドなどを恐れていましたが、GC 言語では、GC を強制するか、代わりに GC の実行を妨げています。
ほとんどのガイドラインと優れた経験則 (および優れた設計プラクティス) の場合と同様に、確立された標準を回避することが理にかなっている場合はほとんどありません。ケースを理解していること、あなたのケースが実際に一般的な慣行の廃止を必要としていること、そしてあなたが引き起こす可能性のあるリスクと副作用を理解していることを十分に確認する必要があります. しかし、そのような場合があります。
プログラミングの問題は多岐にわたり、柔軟なアプローチが必要です。ガベージ コレクション言語で GC をブロックすることが理にかなっているケースや、GC が自然に発生するのを待つのではなく、GC をトリガーすることが理にかなっている場所を見てきました。95% の確率で、これらのいずれかが問題に正しくアプローチしていないことを示しています。しかし、20 分の 1 の確率で、正当な理由があると考えられます。
ガベージ コレクションの裏をかこうとしないことを学びました。そうは言っても、using
ファイル I/O やデータベース接続などの管理されていないリソースを扱うときは、キーワードを使用することに固執しています。
私が最近遭遇した手動呼び出しが必要なケースの 1 つはGC.Collect()
、C# からアクセスされる小さなマネージ C++ オブジェクトにラップされた大きな C++ オブジェクトを操作する場合でした。
マネージ メモリの使用量はごくわずかでしたが、アンマネージ メモリの使用量は膨大だったため、ガベージ コレクタが呼び出されることはありませんでした。オブジェクトを手動で呼び出すDispose()
には、オブジェクトがいつ不要になったかを自分で追跡する必要がありますが、呼び出すと、GC.Collect()
参照されなくなったオブジェクトがクリーンアップされます.....
それがベスト プラクティスであるかどうかはわかりませんが、大量の画像をループで処理する場合 (つまり、多くの Graphics/Image/Bitmap オブジェクトを作成して破棄する場合)、定期的に GC.Collect.
プログラムが(ほとんど)アイドル状態のときにのみGCが実行され、集中的なループの途中ではないことをどこかで読んだと思います。そのため、手動GCが理にかなっている領域のように見えます。
あなたはすでにベストプラクティスを挙げていると思いますが、本当に必要でない限り、それを使用しないでください. 最初にこれらの質問に答えるために、必要に応じてプロファイリング ツールを使用して、コードをより詳細に調べることを強くお勧めします。
- 必要以上に大きなスコープでアイテムを宣言しているコードが何かありますか?
- メモリ使用量は本当に高すぎますか
- GC.Collect() を使用する前後のパフォーマンスを比較して、本当に効果があるかどうかを確認してください。
プログラムにメモリリークがなく、オブジェクトが蓄積され、Gen 0でGC化できないとします。理由は次のとおりです。1)オブジェクトは長期間参照されるため、Gen1とGen2に入ります。2)それらは大きなオブジェクト(> 80K)なので、LOH(Large Object Heap)に入ります。また、LOHは、Gen0、Gen1、およびGen2のように圧縮を行いません。
「.NETMemory」のパフォーマンスカウンターを確認すると、1)問題は実際には問題ではないことがわかります。通常、10個のGen0GCごとに1個のGen1GCがトリガーされ、10個のGen1GCごとに1個のGen2GCがトリガーされます。理論的には、GC0にプレッシャーがない場合(プログラムのメモリ使用量が実際に配線されている場合)、GC1とGC2をGCすることはできません。それは私には決して起こりません。
問題2)については、「。NETメモリ」パフォーマンスカウンターをチェックして、LOHが肥大化しているかどうかを確認できます。それが本当に問題の問題である場合は、このブログがhttp://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspxを示唆しているように、おそらく大きなオブジェクトプールを作成できます。
私はそれを追加したいと思います: 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() を明示的に呼び出す必要があります。それが本質です!:)
ラージ オブジェクトは、ジェネレーション 0 ではなく、LOH (ラージ オブジェクト ヒープ) に割り当てられます。ジェネレーション 0 ではガベージ コレクションされないというのであれば、その通りです。完全な GC サイクル (ジェネレーション 0、1、および 2) が発生した場合にのみ収集されると思います。
そうは言っても、大規模なオブジェクトを操作し、メモリの負荷が高まっている場合、GC はより積極的にメモリを調整して収集すると考えています。
収集するかどうか、およびどのような状況で収集するかを言うのは難しい. 多数のコントロールなどを含むダイアログウィンドウ/フォームを破棄した後、以前は GC.Collect() を実行していました (フォームとそのコントロールが、ビジネスオブジェクトの多くのインスタンスを作成する/大量のデータをロードするために第 2 世代になるまでに-いいえ)大きなオブジェクトは明らかに)、しかし実際には、そうすることによる長期的なプラスまたはマイナスの影響に気づきませんでした.
もう1つ、GC Collectを明示的にトリガーしても、プログラムのパフォーマンスが向上しない場合があります。それを悪化させる可能性は十分にあります。
.NET GCは、適応性があるように適切に設計および調整されています。つまり、プログラムのメモリ使用量の「習慣」に応じてGC0/1/2のしきい値を調整できます。したがって、しばらく実行すると、プログラムに適合します。GC.Collectを明示的に呼び出すと、しきい値がリセットされます。そして、.NETは、プログラムの「習慣」に再び適応するために時間を費やす必要があります。
私の提案は常に.NETGCを信頼することです。メモリの問題が表面化した場合は、「。NETメモリ」パフォーマンスカウンタを確認して、自分のコードを診断してください。
それがベストプラクティスかどうかはわかりません...
提案: 不明な場合は、これまたは何かを実装しないでください。事実が判明した時点で再評価し、検証のために前後のパフォーマンス テストを実行します。
ただし、コードを確実にテストして、 Collect() の呼び出しが悪影響を及ぼさないことを確認できる場合は、先に進んでください...
私見ですが、これは「あなたのプログラムに将来バグがないことを証明できるなら、先に進んでください...」と言っているのと似ています。
真面目な話、GC の強制はデバッグ/テストの目的に役立ちます。それ以外のときにそれを行う必要があると感じる場合は、間違っているか、プログラムが間違って作成されているかのいずれかです。いずれにせよ、解決策はGCを強制していません...