web-dev-qa-db-ja.com

ガベージコレクションを強制するのはいつ良い考えですか?

だから私は読んでいました C#ガベージコレクターの実行を強制することに関する質問 ここで、ほぼすべての答えは同じです:あなたはそれを行うことができますが、すべきではありません-一部を除いて非常にまれなケース。悲しいことに、誰もそのような場合について詳しく述べていません。

ガベージコレクションを強制することは、実際にはどのようなシナリオで良いまたは合理的なアイデアであるかを教えていただけますか?

私はC#固有のケースではなく、ガベージコレクターを備えたすべてのプログラミング言語を求めています。 Javaのようなすべての言語でGCを強制することはできないことはわかっていますが、そうすることができるとしましょう。

138
Omega

allGC実装を適切に使用する方法についての包括的なステートメントを実際に作成することはできません。それらは大きく異なります。それで、あなたが最初に言及した.NETのものに話をします。

ロジックや理由でこれを行うには、GCの動作をかなり詳しく知っている必要があります。

私が与えることができるコレクションに関する唯一のアドバイスは次のとおりです:絶対にしないでください。

GCの複雑な詳細を本当に知っている場合は、私のアドバイスは必要ないので、問題にはなりません。 100%自信を持ってまだわからない場合は役に立ちます。オンラインで次のような答えを見つける必要があります。GC.Collectを呼び出すことはできませんまたは、代わりにGCがどのように動作するかを詳細に学習し、それから初めて知って答えを知ることができます。

GC.Collectを使用するのに意味のある安全な場所が1つあります

GC.Collectは、物事のタイミングのプロファイリングに使用できるAPIです。 2番目のアルゴリズムが結果を歪めている間に最初のアルゴのGCが発生していないことを知った直後に、1つのアルゴリズムをプロファイルし、収集して、別のアルゴリズムをプロファイルできます。

この種のプロファイリングは、手動で収集することを誰かに勧めるのは初めてのことです。


とにかく人為的な例

考えられる使用例の1つは、非常に大きなものをロードすると、最終的にラージオブジェクトヒープになり、Gen 2に直接移動しますが、Gen 2は、収集頻度が低いため、長期間有効なオブジェクト用です。何らかの理由で有効期間が短いオブジェクトをGen 2にロードしていることを知っている場合は、それらをより早くクリアしてGen 2を小さく保ち、コレクションを高速化できます。

これは私が思いつくことができる最良の例であり、それは良いことではありません-ここで作成しているLOHプレッシャーはより頻繁なコレクションを引き起こし、コレクションは頻繁であるので-LOHをクリアする可能性があります一時的なオブジェクトでそれを吹き飛ばしていたように速く。 myselfを信頼せず、GC自体よりも良い収集頻度を推定します-私よりもはるかに賢いfar人々によって調整されます。


それでは、.NET GCのセマンティクスとメカニズムのいくつかについて話しましょう...または...

.NET GCについて知っているすべてと思います

ここでエラーを見つけた人は誰でも-私を訂正してください。 GCの多くは黒魔術であることがよく知られているので、不明な点を省こうとしたが、おそらくまだ何か問題があった。

以下にはわからない多くの詳細がわざと欠けているだけでなく、私が単に気付いていないはるかに多くの情報が含まれています。この情報は自己責任で使用してください。


GCの概念

.NET GCは一貫性のない時間に発生するため、「非決定的」と呼ばれています。これは、特定の時間に発生することを信頼できないことを意味します。また、世代別ガベージコレクタでもあります。つまり、オブジェクトを、通過したGCパスの数に分割します。

Gen 0ヒープ内のオブジェクトは0コレクションを介して存続し、これらは新しく作成されたため、インスタンス化以降、コレクションは発生していません。 Gen 1ヒープ内のオブジェクトは1つのコレクションパスを通過し、同様にGen 2ヒープ内のオブジェクトは2つのコレクションパスを通過しました。

ここで、これらの特定の世代とパーティションを適切に修飾する理由に注目する価値があります。 .NET GCはこれらの3つの世代のみを認識します。これは、これらの3つのヒープを通過するコレクションパスがすべてわずかに異なるためです。一部のオブジェクトは、コレクションパスを数千回通過しても存続する場合があります。 GCはこれらをGen 2ヒープパーティションの反対側に残すだけであり、実際にはGen 44であるため、さらにパーティション化しても意味がありません。それらのコレクションパスは、Gen 2ヒープのすべてと同じです。

これらの特定の世代には意味上の目的と、これらを尊重する実装されたメカニズムがあり、それらについてはすぐに説明します。


コレクションの内容

GCコレクションパスの基本的な概念は、ヒープスペース内の各オブジェクトをチェックして、これらのオブジェクトへのライブ参照(GCルート)がまだあるかどうかを確認することです。オブジェクトのGCルートが見つかった場合は、現在実行中のコードがそのオブジェクトに到達して使用できる可能性があるため、削除できません。ただし、オブジェクトのGCルートが見つからない場合は、実行中のプロセスでオブジェクトが不要になったため、オブジェクトを削除してメモリを解放し、新しいオブジェクト用に解放できます。

大量のオブジェクトのクリーンアップが完了し、一部を残した後、不幸な副作用が発生します:死んだオブジェクトが削除されたライブオブジェクト間の空きスペースのギャップ。このメモリの断片化は、そのままにしておくとメモリを浪費するだけなので、コレクションは通常、「圧縮」と呼ばれる処理を実行して、すべてのライブオブジェクトを残してヒープにまとめ、空きメモリがヒープの片側でGenに隣接するようにします。 0。

ここで、3ヒープのメモリという考えが与えられ、すべてが通過したコレクションパスの数によってすべて分割されます。これらのパーティションが存在する理由について話しましょう。


Gen 0コレクション

Gen 0は絶対に最新のオブジェクトであり、非常に小さくなる傾向があるため、安全に収集できます非常に頻繁。頻度により、ヒープは小さくとどまり、コレクションは非常に高速になります。これは、そのような小さなヒープで収集しているためです。これは多かれ少なかれヒューリスティックに基づいています:あなたが作成するtemporaryオブジェクトの大部分はvery一時的です、そのため、一時的には使用後すぐに使用または参照されなくなり、収集できます。


第1世代コレクション

Gen 1は、オブジェクトのこのvery一時カテゴリに分類されなかったオブジェクトですが、作成されたオブジェクトの大部分は、長いです。したがって、Gen 1も比較的頻繁に収集し、ヒープを小さくして、収集が高速になるようにします。ただし、仮定はそのオブジェクトのlessがGen 0より一時的であるため、Gen 0よりも収集頻度が低くなります。

Gen 0の収集パスとGen 1の収集パスの収集頻度以外の違いがある場合、それらの技術メカニズムの違いを率直に知らないと私は言います。


Gen 2コレクション

第2世代はすべてのヒープの母であるに違いありませんか?まあ、はい、それは多かれ少なかれ正しいです。これは、すべての永続オブジェクトが存在する場所です。たとえば、Main()が存在するオブジェクトと、Main()が参照するすべてのオブジェクトは、プロセスの最後にMain()が返されるまでルートされるためです。

Gen 2は基本的に他の世代が収集できなかったすべてのもののバケットであることを考えると、オブジェクトは大部分が永続的であるか、少なくとも長命です。したがって、Gen 2の内容のほとんどを認識しなくても、実際には収集できるものになるため、頻繁に収集する必要はありません。これにより、実行頻度が大幅に低下するため、コレクションの速度も低下します。つまり、奇妙なシナリオの場合、基本的にここで追加のすべての動作に取り組みました。これは、実行する時間があるためです。


ラージオブジェクトヒープ

Gen 2の追加の動作の1つの例は、ラージオブジェクトヒープでのコレクションを行うことです。これまでは、スモールオブジェクトヒープについて完全に説明してきましたが、.NETランタイムは、上記の圧縮と呼んでいるため、特定のサイズのものを別のヒープに割り当てます。圧縮では、小さいオブジェクトヒープでコレクションが終了したときにオブジェクトを移動する必要があります。 Gen 1に生きている10MBのオブジェクトがある場合、コレクション後の圧縮が完了するまでに時間がかかり、Gen 1のコレクションの速度が低下します。そのため、10mbオブジェクトがラージオブジェクトヒープに割り当てられ、めったに実行されないGen 2の間に収集されます。


ファイナライズ

別の例は、ファイナライザを備えたオブジェクトです。 .NETのGCの範囲外のリソース(アンマネージリソース)を参照するオブジェクトにファイナライザーを配置します。ファイナライザは、GCが管理されていないリソースの収集を要求する唯一の方法です。ファイナライザを実装して、管理されていないリソースを手動で収集/削除/解放して、プロセスからリークしないようにします。 GCがオブジェクトファイナライザの実行を開始すると、実装はアンマネージリソースをクリアし、GCがリソースリークのリスクなしにオブジェクトを削除できるようにします。

ファイナライザがこれを行うメカニズムは、ファイナライズキューで直接参照されます。ランタイムがファイナライザを使用してオブジェクトを割り当てると、そのオブジェクトへのポインタがファイナライズキューに追加され、オブジェクトが所定の位置に固定されます(ピン留めと呼ばれます)。これにより、圧縮によってオブジェクトが移動されず、ファイナライズキューの参照が壊れます。コレクションパスが発生すると、最終的にオブジェクトにはGCルートがないことがわかりますが、ファイナライズを実行してからでないと収集できません。そのため、オブジェクトが無効になると、コレクションはその参照をファイナライズキューから移動し、「FReachable」キューと呼ばれるものにその参照を配置します。その後、コレクションは続行されます。将来の別の「非決定的」時に、ファイナライザスレッドと呼ばれる別のスレッドがFReachableキューを通過し、参照される各オブジェクトのファイナライザを実行します。それが完了すると、FReachableキューは空になり、ファイナライズは不要であると各オブジェクトのヘッダーで少し反転しました(このビットは、Dispose()メソッドで一般的なGC.SuppressFinalizeを使用して手動で反転することもできます)。 容疑者はオブジェクトの固定を解除しましたが、それについて私を引用しないでください。このオブジェクトが含まれているヒープで発生する次のコレクションは、最終的にそれを収集します。 Gen 0コレクションは、ファイナライズが必要なビットがオンになっているオブジェクトに注意を払うことさえせず、ルートをチェックすることなく、自動的にそれらを昇格させます。 Gen 1でファイナライズを必要とするルート化されていないオブジェクトはFReachableキューで破棄されますが、コレクションはそれを何も実行しないため、Gen 2に存在します。このようにして、ファイナライザ、およびGC.SuppressFinalizeはGen 2で収集されません。

126
Jimmy Hoffa

悲しいことに、誰もそのような場合について詳しく述べていません。

いくつか例を挙げます。全体として、GCを強制するのが良い考えであることはまれですが、それだけの価値があります。この答えは、.NETとGCに関する文献での私の経験に基づいています。他のプラットフォーム(少なくとも、重要なGCがあるプラットフォーム)に一般化する必要があります。

  • ベンチマークさまざまな種類。 GCがベンチマーク中にランダムにトリガーされないように、ベンチマークの開始時に既知のマネージヒープ状態が必要です。ベンチマークを繰り返すときは、繰り返しごとに同じ数と量のGC作業が必要です。
  • リソースの突然の解放。たとえば、重要なGUIウィンドウを閉じる、またはキャッシュを更新する(それにより、潜在的に大きなキャッシュの内容を解放する)。参照をnullに設定しているだけなので、GCはこれを検出できません。これがオブジェクトグラフ全体を孤立させるという事実は、簡単には検出できません。
  • リークしたアンマネージリソースのリリース。もちろん、これは決して発生してはならないことですが、サードパーティのライブラリが(COMオブジェクトなどの)情報を漏えいするケースを見てきました。開発者は時々コレクションを誘発することを余儀なくされました。
  • ゲームなどのインタラクティブなアプリケーション。プレイ中のゲームでは、フレームごとに非常に厳密なタイムバジェットがあります(60Hz =>フレームあたり16ミリ秒)。問題を回避するには、GCに対処するための戦略が必要です。そのような戦略の1つは、G2 GCを可能な限り遅延させ、ロード画面やカットシーンなどの適切なタイミングで強制的に強制することです。 GCは、そのような最高の瞬間がいつであるかを知ることができません。
  • レイテンシコントロール一般。一部のウェブアプリケーションは、GCを無効にし、ロードバランサーローテーションから切り替えられている間、定期的にG2コレクションを実行します。これにより、G2レイテンシがユーザーに表示されることはありません。

目標がスループットである場合、GCは少ないほうが良いです。これらの場合、コレクションを強制しても効果はありません(CPUキャッシュの使用率が増加するなど、不自然なオブジェクトが削除され、CPUキャッシュの使用率が高くなるなどの問題は除きます)。ライブのもの)。バッチ収集は、私が知っているすべてのコレクターにとってより効率的です。定常状態のメモリ消費で本番アプリの場合、GCを誘導しても効果がありません。

上記の例では、メモリ使用量の一貫性と制限をターゲットにしています。これらの場合、誘導されたGCは理にかなっています。

GCは本当に最適である場合はいつでもコレクションを誘導する神の実体であるという広範な考えがあるようです。私が知っているGCは、その洗練されたものであり、実際にGCに最適化することは非常に困難です。 GCが知っているのは開発者よりも少ないです。ヒューリスティックはメモリカウンタと収集率などに基づいています。通常、ヒューリスティックスは優れていますが、大量のマネージメモリの解放など、アプリケーションの動作の突然の変化をキャプチャしません。また、管理されていないリソースやレイテンシの要件も考慮されていません。

GCのコストは、ヒープサイズとヒープ上の参照の数によって異なることに注意してください。小さなヒープでは、コストは非常に小さくなります。 .NET 4.5で1 GBのヒープサイズの本番用アプリで1〜2 GB /秒のG2コレクションレートを確認しました。

67
usr

一般的な原則として、ガベージコレクターは「メモリ不足」に陥ったときに収集します。パフォーマンスの問題を引き起こしたり、プログラムの実行中に目立った一時停止を引き起こしたりする可能性があるため、他の時間には収集しないことをお勧めします。実際、最初のポイントは2番目のポイントに依存します。少なくとも世代別のガベージコレクタの場合、 適切なオブジェクトに対するガベージの比率が高くなるほど効率的に実行されます したがって、プログラムの一時停止に費やされた時間、それは プロクラスチン化し、可能な限りゴミを山積みにします。

ガベージコレクターを手動で呼び出す適切なタイミングは、1)大量のガベージが作成された可能性があり、2)時間がかかり、システムを応答しないままにしておくことが期待されている処理を完了したときです。とにかく。古典的な例は、大きなもの(ドキュメント、モデル、新しいレベルなど)のロードの最後です。

27
Mason Wheeler

誰も言及していないことの1つは、Windows GCは驚くほど優れているが、XboxのGCはガベージ(意図された)であることです。

そのため、XBoxで実行することを目的としたXNAゲームをコーディングする場合、ガベージコレクションを適切なタイミングで行うことが絶対に重要です。さらに、XBoxでは、ガベージコレクションが必要なオブジェクトの数を最小限に抑えるために、通常よりも頻繁にstructsを使用するのが一般的です。

ガベージコレクションは、何よりもまずメモリ管理ツールです。そのため、ガベージコレクターはメモリのプレッシャーがあるときに収集されます。

現代のガベージコレクターは非常に優れており、改善されているため、手動で収集して改善することはほとんどありません。今日の状況を改善できたとしても、選択したガベージコレクターの将来の改善により、最適化が無効になったり、逆効果になったりする場合もあります。

ただし、ガベージコレクターは通常、メモリ以外のリソースの使用を最適化しようとしません。ガベージコレクションされた環境では、最も価値のある非メモリリソースにはcloseメソッドなどがありますが、既存のAPIとの互換性など、何らかの理由でこれが当てはまらない場合があります。

このような場合、メモリ以外の貴重なリソースが使用されていることがわかっている場合は、ガベージコレクションを手動で呼び出すことをお勧めします。

RMI

この1つの具体例は、Javaのリモートメソッド呼び出しです。 RMIはリモートプロシージャコールライブラリです。通常、サーバーがあり、クライアントがさまざまなオブジェクトを使用できるようにします。オブジェクトがどのクライアントにも使用されていないことをサーバーが認識している場合、そのオブジェクトはガベージコレクションの対象になります。

ただし、サーバーがこれを知る唯一の方法は、クライアントがそれを伝えるかどうかであり、クライアントがサーバーにオブジェクトを必要としないことを伝えるのは、クライアントがそれを使用しているガベージコレクションを実行した後です。

クライアントには多くの空きメモリがあるため、ガベージコレクションが頻繁に実行されない可能性があるため、これには問題があります。その間、サーバーはメモリにたくさんの未使用のオブジェクトを持っているかもしれません、それはクライアントがそれらを使用していないことを知らないので収集することができません。

RMIの解決策は、メモリが十分にある場合でも、クライアントがガベージコレクションを定期的に実行して、オブジェクトがサーバー上で迅速に収集されるようにすることです。

4
James_pic

実際の例:

ほとんど変更されず、非常に迅速にアクセスする必要がある非常に大きなデータセットを使用するWebアプリケーションがありました(AJAXを使用したキーストロークごとの応答には十分迅速)。

ここで行うべき明らかなことは、関連するグラフをメモリにロードし、データベースではなくそこからアクセスして、DBが変更されたときにグラフを更新することです。

しかし、非常に大きいと、将来の増大により、単純な負荷がデータとともに少なくとも6GBのメモリを占有することになります。 (正確な数値はありませんが、私の2 GBのマシンが少なくとも6 GBに対応しようとしていることが明らかになったら、それが機能しないことを知るために必要なすべての測定を行いました)。

幸いなことに、このデータセットには、互いに同じ数のアイスキャンディー不変オブジェクトが含まれていました。特定のバッチが別のバッチと同じであることがわかったら、ある参照を別の参照にエイリアスして、大量のデータを収集できるようにして、すべてを半分未満のギグに収めることができました。

順調ですが、これでも、この状態に到達するために、約30分のスペースで6 GBを超えるオブジェクトを使用しています。そのままでは、GCは対応できませんでした。アプリケーションの通常のパターン(1秒あたりの割り当て解除の負荷がはるかに少ない)を超えるアクティビティの急上昇は、あまりにも急激でした。

したがって、このビルドプロセス中に定期的にGC.Collect()を呼び出すと、すべてがスムーズに機能します。もちろん、アプリケーションの実行中、残りの時間に手動でGC.Collect()を呼び出すことはしませんでした。

この実際のケースは、GC.Collect()を使用する必要がある場合のガイドラインの良い例です。

  1. 大量のオブジェクトを収集できる比較的まれなケースで使用します(メガバイト相当が使用可能になり、このグラフ作成は、アプリケーションの存続期間中(週に約1分)非常にまれなケースでした。
  2. パフォーマンスの低下が比較的許容できる場合に実行してください。これは、アプリケーションの起動時にのみ発生しました。 (このルールのもう1つの良い例は、ゲーム中のレベル間、またはプレーヤーが少しのポーズで動揺しないゲームの他のポイントの間です)。
  3. 本当に改善があることを確認するためのプロファイル。 (かなり簡単です。「機能する」は、ほとんどの場合「機能しない」を上回ります)。

ポイント1と2が適用されたため、GC.Collect()を呼び出す価値がある場合があると私が思っていたほとんどの場合、ポイント3は、事態を悪化させるか、少なくとも事態を改善しなかった(そして改善がほとんどまたはまったくない場合は、アプリケーションの存続期間を通じてより適切に証明される可能性が高いため、電話をかけ直さないようにします。

2
Jon Hanna

自分でgc()を呼び出したい場合がいくつかあります。

  • [古い世代の空間にオブジェクトを昇格させる可能性があるため、これは良くないと私が同意する人もいます。ただし、昇格できるオブジェクトが常に存在することは[〜#〜] not [〜#〜]常にtrueです。このgc()の呼び出し後、古い世代のスペースに移動されることはもちろん、非常に少数のオブジェクトが残っている可能性もあります]オブジェクトの大きなコレクションを作成するときに、大量のメモリを使用します。あなたは単にできるだけ多くの準備をできるだけ空けたいと思っています。これは単なる常識です。 gc()を手動で呼び出すことにより、メモリにロードしているオブジェクトの大きなコレクションの一部に対して冗長な参照グラフチェックが行われなくなります。つまり、メモリに大量にロードする前にgc()を実行すると、ロード中に誘発されるgc()は、ロード時にメモリプレッシャーが発生するときに少なくとも1回は発生しません。
  • bigコレクションのロードが完了したら 大きい オブジェクトとメモリにもっと多くのオブジェクトをロードすることはまずありません。つまり、フェーズの作成からフェーズの使用に移ります。実装に応じてgc()を呼び出すことにより、使用中のメモリが圧縮され、キャッシュの局所性が大幅に向上します。 これにより、プロファイリングでは得られないパフォーマンスが大幅に向上します
  • 最初のものと似ていますが、gc()を実行し、メモリ管理の実装がサポートする場合、物理メモリの継続性が大幅に向上するという観点からです。これにより、オブジェクトの新しい大きなコレクションがより継続的でコンパクトになり、パフォーマンスが向上します
2
InformedA

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

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

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

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

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

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

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

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

Rico Marianiのパフォーマンスのヒント 」も参照してください。

2
Ian

やや正統でないゴミ処理の使い方があります。

IDisposable-disposingとして知られている、醜く、不格好で、エレガントでなく、エラーが発生しやすいイディオムを使用してオブジェクトの破棄を実装するという、C#の世界では残念ながらこの見当違いの習慣があります。 MSDN 長さで説明します 、そして多くの人々はそれを誓い、それを信心深くフォローし、何時間にもわたって正確にどうやってすべきかについて議論しているなど。

(ここで醜いと呼んでいるのは、オブジェクトの破棄パターン自体ではないことに注意してください。醜いと呼んでいるのは、特定のIDisposable.Dispose( bool disposing )イディオムです。)

このイディオムは、オブジェクトのデストラクタがガベージコレクタによって常に呼び出されてリソースをクリーンアップすることを保証することが不可能であるため、発明されました。そのため、人々はIDisposable.Dispose()内でリソースのクリーンアップを実行し、忘れた場合はまた、デストラクタ内からもう一度試してみます。念のためです.

ただし、IDisposable.Dispose()には、クリーンアップするマネージドオブジェクトとアンマネージドオブジェクトの両方が含まれる場合がありますが、IDisposable.Dispose()がデストラクタ内から呼び出された場合、マネージドオブジェクトはクリーンアップできません。その時点でガベージコレクターが処理するため、_bool disposing_フラグを受け入れる個別のDispose()メソッドが必要であり、マネージオブジェクトとアンマネージオブジェクトの両方をクリーンアップする必要があるかどうかを確認します。または管理されていないもののみ。

すみませんが、これは正気ではありません。

アインシュタインの公理は、物事は可能な限り単純でなければならないが、単純ではない、と述べています。明らかに、リソースのクリーンアップを省略できないため、最も単純なソリューションには少なくともそれを含める必要があります。次の最も簡単な解決策は、代替となるフォールバックとしてデストラクタに依存することで物事を複雑にすることなく、破棄されることになっている正確な時間にすべてを常に破棄することです。

厳密に言えば、もちろん、プログラマがIDisposable.Dispose()の呼び出しを忘れて間違いを犯さないことを保証することは不可能ですが、できることはデストラクタを使用してこの間違いをキャッチします。本当に簡単です。使い捨てオブジェクトのdisposedフラグがtrueに設定されていないことを検出すると、デストラクタはログエントリを生成するだけです。したがって、デストラクタの使用は、廃棄戦略の不可欠な部分ではありませんが、品質保証メカニズムです。また、これはデバッグモードのみのテストであるため、デストラクタ全体を_#if DEBUG_ブロック内に配置できるため、本番環境では破壊ペナルティは発生しません。 (IDisposable.Dispose( bool disposing )イディオムは、ファイナライズのオーバーヘッドを減らすためにGC.SuppressFinalize()を正確に呼び出す必要があることを規定していますが、私のメカニズムでは、本番環境でのオーバーヘッドを完全に回避できます。)

つまり、永続的なハードエラーとソフトエラー引数です。IDisposable.Dispose( bool disposing )イディオムはソフトエラーアプローチです。そして、可能であれば、システム障害を起こすことなくプログラマがDispose()の呼び出しを忘れることを可能にする試みを表しています。ハードエラーアプローチでは、プログラマーは常にDispose()が呼び出されることを確認する必要があると述べています。ほとんどの場合、ハードエラーアプローチによって通常規定されるペナルティはアサーションの失敗ですが、この特定のケースでは例外を作成し、単純なエラーログエントリの発行に対するペナルティを軽減します。

したがって、このメカニズムが機能するためには、アプリケーションのデバッグバージョンが終了する前に完全なガベージ処理を実行して、すべてのデストラクタが呼び出されることを保証し、忘れたIDisposableオブジェクトをキャッチする必要があります。処分する。

0
Mike Nakis

どのようなシナリオで、ガベージコレクションを強制するのが実際に良いか合理的なアイデアであるか教えてもらえますか?私はC#固有のケースではなく、ガベージコレクターを備えたすべてのプログラミング言語を求めています。 Javaのようなすべての言語でGCを強制することはできないことはわかっていますが、そうすることができるとしましょう。

非常に理論的に言えば、一部のGC実装の​​ような問題を無視して、コレクションサイクル中に物事が遅くなる、ガベージコレクションを強制するために考えられる最大のシナリオは、たとえば、クラッシュするため、論理的なリークがぶら下がるポインタークラッシュよりも望ましいミッションクリティカルなソフトウェアです。予期しない時に人命やこの種の何かを犠牲にする可能性があります。

FlashゲームのようなGC言語を使用して書かれたおかしなインディーゲームのいくつかを見ると、それらは狂ったようにリークしますが、クラッシュしません。ゲームのコードベースの一部が参照をnullに設定するか、リストから削除するのを忘れたため、ゲームをプレイするのに20分の10倍のメモリがかかる場合があり、フレームレートが低下し始める可能性がありますが、ゲームは引き続き機能します。粗雑なCまたはC++コーディングを使用して記述された同様のゲームは、同じタイプのリソース管理ミスの結果として、ダングリングポインターにアクセスした結果としてクラッシュする可能性がありますが、それほどリークすることはありません。

ゲームの場合、クラッシュはすぐに検出して修正できるという意味で望ましいかもしれませんが、ミッションクリティカルなプログラムの場合、まったく予期しないときにクラッシュすると、誰かを殺す可能性があります。したがって、クラッシュしないか、他のいくつかのフォームがセキュリティであるシナリオが絶対的に重要であると考えられる主なケースであり、論理的なリークは、比較すると比較的些細なことです。

GCを強制するのが悪いと思う主なシナリオは、論理的なリークが実際にクラッシュよりも望ましくない場合です。たとえばゲームの場合、クラッシュは必ずしも誰もを殺すわけではなく、内部テスト中に簡単にキャッチして修正できるかもしれませんが、論理的なリークは、製品が出荷された後でも、数分以内にゲームをプレイできないようにしない限り、気付かれない可能性があります。一部のドメインでは、テストで発生する簡単に再現可能なクラッシュは、誰もすぐには気付かないリークよりも好ましい場合があります。

チームでGCを強制する方が好ましいと思われるもう1つのケースは、コマンドラインから実行されて1つのタスクを実行してシャットダウンするような非常に短期間のプログラムの場合です。その場合、プログラムの存続期間は短すぎて、あらゆる種類の論理リークを簡単にできません。大きなリソースであっても、論理リークは通常、ソフトウェアの実行後数時間または数分で問題になるだけなので、3秒間だけ実行するように設計されたソフトウェアが論理リークの問題を抱えることはほとんどありません。チームがGCを使用しただけの場合、このような短期間のプログラムを書く方が簡単です。

0
user204677