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


135

だから私は、C#ガベージコレクターを強制的に実行することについての質問を読んでい。ほとんどすべての回答が同じである。残念ながら、そのようなケースについて詳しく説明している人はいません。

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

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


17
「しかし、むしろ、ガベージコレクタを備えたすべてのプログラミング言語」異なる言語(より適切には異なる実装)は、ガベージコレクションに異なるメソッドを使用するため、万能のルールを見つけることはほとんどありません。
サーティーツー大佐

4
@Dovalリアルタイムの制約下にあり、GCがマッチングの保証を提供しない場合、あなたは岩と厳しい場所の間にいます。何もしないよりも望ましくない一時停止を減らすことができますが、私が聞いたところからは、通常の操作で割り当てを避ける方が「簡単」です。

3
あなたが厳しいリアルタイムの締め切りを期待しているなら、そもそもGC言語を使うことはないだろうという印象を受けました。
グレッグロス

4
VM固有ではない方法でこれに答える方法がわかりません。32ビットプロセスに関連し、64ビットプロセスには無関係です。.NET JVMおよびハイエンドの場合
-rwong

3
@DavidConradでは、C#で強制できます。したがって、質問。
オメガ

回答:


127

すべての GC実装を使用する適切な方法について、包括的な説明をすることはできません。それらは大きく異なります。そこで、最初に言及した.NETの1つについてお話します。

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

コレクションに関する唯一のアドバイスは、絶対にやらないことです。

GCの複雑な詳細を本当に知っている場合は、私のアドバイスは必要ないので、それは重要ではありません。すでに100%の自信を持ってわからない場合、それは助ける、およびオンライン見ていると、このような答えを見つける:あなたはGC.Collectを呼ぶべきではない、あるいは:あなたはどのようにGC作品の詳細を学ぶ行くべき内と外、そしてその時だけあなたは答えを知るでしょう

GC.Collectを使用するのが理にかなっている安全な場所が1つあります

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

この種のプロファイリングは、私が誰にでも手動で収集することを提案するのはこれまでに一度だけです。


とにかく不自然な例

考えられる使用例の1つは、非常に大きなものをロードすると、それらが最終的にGen 2に到達するLarge Object Heapになりますが、Gen 2は収集頻度が低いため、長寿命のオブジェクト用です。あなたがいる場合知っているあなたが何らかの理由でのGen 2に短命オブジェクトをロードしていることを、あなたはあなたのGen 2より小さいを維持するために、より迅速にそれらをクリアすることができ、それはより速く、コレクションです。

これは私が思いつく最高の例であり、それは良くありません-ここで構築しているLOHのプレッシャーはより頻繁なコレクションを引き起こし、コレクションはそう頻繁にあります-チャンスはLOHをクリアすることです一時オブジェクトで吹き飛ばしていたのと同じくらい速い。私は単純に信頼していない自分自身を GC自体よりも優れた収集頻度を推定する-これまで人々によってチューニングされたこれまで賢くI.より


.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ルートが見つからない場合、実行中のプロセスはオブジェクトを必要としないため、そのオブジェクトを削除して新しいオブジェクト用のメモリを解放できます。

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

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


Gen 0コレクション

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


Gen 1コレクション

Gen 1は、この非常に一時的なオブジェクトのカテゴリに分類されなかったオブジェクトですが、作成されたオブジェクトの大部分が長期間使用されないため、まだ短命かもしれません。そのため、Gen 1もかなり頻繁に収集し、再びヒープを小さくして、収集が高速になるようにします。ただし、Gen 0よりも一時的なオブジェクトの仮定が少ないため、Gen 0よりも収集頻度が低くなります。

Gen 0の収集パスとGen 1の収集メカニズムの違いは、収集する頻度以外にまったく存在しない場合、率直に言ってわかりません。


Gen 2コレクション

Gen 2は現在、すべてのヒープの母である必要がありますか?まあ、はい、それは多かれ少なかれ正しいです。すべての永続オブジェクトが存在する場所Main()です。たとえば、あなたが住んでいるオブジェクト、およびプロセスの最後に戻るMain()までルート化されるため参照するすべてのものですMain()

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


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

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


ファイナライズ

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

ファイナライザがこれを行うメカニズムは、ファイナライズキューで直接参照されることです。ランタイムがファイナライザーを使用してオブジェクトを割り当てると、そのオブジェクトへのポインターをファイナライズキューに追加し、オブジェクトを所定の位置にロックします(ピン留めと呼ばれます)。収集パスが発生すると、最終的にオブジェクトにGCルートがないことがわかりますが、収集する前にファイナライズを実行する必要があります。そのため、オブジェクトがデッドになると、コレクションはその参照をファイナライズキューから移動し、「FReachable」キューと呼ばれるものに参照を配置します。その後、収集が続行されます。将来の別の「非決定論的な」時間に、Finalizerスレッドと呼ばれる別のスレッドがFReachableキューを通過し、参照されるオブジェクトごとにファイナライザーを実行します。完了すると、FReachableキューは空になり、各オブジェクトのヘッダーで、ファイナライズを必要としないと言うビットが反転しました(このビットは手動で反転することもできますGC.SuppressFinalizeDispose()メソッドでは一般的です)、オブジェクトの固定を解除したのではないかと疑っていますが、それについては引用しないでください。このオブジェクトがどのヒープにある場合でも、次のコレクションが最終的に収集します。Gen 0コレクションは、ファイナライズが必要なビットをオンにしたオブジェクトにも注意を払わず、ルートを確認することなく自動的にそれらを昇格させます。Gen 1でファイナライズを必要とするルート化されていないオブジェクトはFReachableキューに放り込まれますが、コレクションはそれで何もしないため、Gen 2に残ります。このように、ファイナライザーを持ち、GC.SuppressFinalizeGen 2で収集されます。


4
すべての実装間で「GC」について何が本当に..意味がないと言って...ええ@FlorianMargaine
ジミー・ホッファ

10
tl; dr:代わりにオブジェクトプールを使用します。
ロバートハーヴェイ

5
tl; dr:タイミング/プロファイリングには有用です。
-kutschkem

3
@Denは、上記のメカニックの説明を読んだ後(私が理解しているように)、あなたがそれを見たときの利点は何でしょうか?SOH(またはLOH?)で大量のオブジェクトを削除しますか?このコレクションで他のスレッドを一時停止させただけですか?そのコレクションは、クリアされたオブジェクトの2倍をGen 2にプロモートしましたか?コレクションはLOHで圧縮を引き起こしましたか(それを有効にしましたか?)。GCヒープはいくつありますか?また、GCはサーバーモードまたはデスクトップモードですか?GCは氷山であり、裏切りは水面下にあります。明確に舵を取るだけです。私は快適に収集するほど頭が良くありません。
ジミーホファ

4
@RobertHarveyオブジェクトプールも特効薬ではありません。ガベージコレクターのジェネレーション0は、既に事実上オブジェクトプールです。通常、最小のキャッシュレベルに収まるサイズになっているため、通常、既にキャッシュにあるメモリに新しいオブジェクトが作成されます。オブジェクトプールはGCのキャッシュのナーサリと競合しています。GCのナーサリとプールの合計がキャッシュよりも大きい場合、明らかにキャッシュミスが発生します。そして、並列処理の使用を計画している場合、同期を再実装し、誤った共有を心配する必要があります。
ドーバル

68

残念ながら、そのようなケースについて詳しく説明している人はいません。

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

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

目標がスループットである場合、GCが少ないほど良いです。これらの場合、コレクションの強制はプラスの影響を与えることはできません(ライブオブジェクトに散在しているデッドオブジェクトを削除してCPUキャッシュの使用率を上げるなど、かなり不自然な問題を除く)。バッチコレクションは、私が知っているすべてのコレクターにとってより効率的です。GCを誘発する定常状態のメモリ消費量の実稼働アプリの場合は役に立ちません。

上記の例は、メモリ使用の一貫性と範囲を対象としています。これらの場合、誘導GCは理にかなっています。

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

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


レイテンシ制御の場合、定期的にこれを行う代わりに、必要に応じて(つまり、メモリ使用量が特定のしきい値を超えたとき)行うこともできます。
パエロエベルマン

3
最後から2番目の段落の+1。一部の人々はコンパイラについて同じ感情を抱いており、ほとんど何でも「時期尚早な最適化」と呼んでいます。私は通常彼らに似たようなことを伝えます。
ホンザブラベック

2
その段落にも+1。他の誰かが書いたコンピュータプログラムは、自分のプログラムのパフォーマンス特性を必然的によく理解しなければならないと考えるのは衝撃的です。
-Mehrdad

1
@HonzaBrabec問題はどちらの場合も同じです。GCまたはコンパイラーよりもよく知っていると思うなら、自分を傷つけるのは非常に簡単です。実際に詳しい場合は、時期尚早ではないことがわかっている場合にのみ最適化しています。
svick

27

一般的な原則として、ガベージコレクターは「メモリ不足」に陥ったときに収集します。パフォーマンスの問題を引き起こしたり、プログラムの実行中に顕著な一時停止を引き起こしたりする可能性があるため、他の時間に収集しないことをお勧めします。実際、最初の点は2番目の点に依存しています。少なくとも、世代別のガベージコレクターでは、プログラムの一時停止に費やされる時間を最小限に抑えるために、優れたオブジェクトに対するガベージの比率が高くなるほど効率的に実行されますごみを可能な限り先延ばしにして積み上げなければなりません。

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


12

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

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


4

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

最新のガベージコレクターは非常に優れており、改善されているため、手動で収集して改善することはできません。今日物事を改善できたとしても、選択したガベージコレクターを将来改善すると、最適化が無効になり、逆効果になる可能性があります。

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

これらの場合、貴重な非メモリリソースが使用されていることがわかっている場合、ガベージコレクションを手動で呼び出すのが理にかなっています。

RMI

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

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

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

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


「これらのケースでは、貴重な非メモリリソースが使用されていることがわかっている場合、ガベージコレクションを手動で呼び出すのが理にかなっている場合があります」-非メモリリソースが使用されている場合、usingブロックを使用するか、Closeメソッドを呼び出すリソースができるだけ早く破棄されるようにします。非メモリリソースをクリーンアップするためにGCに依存することは信頼できず、あらゆる種類の問題を引き起こします(特に、アクセスのためにロックする必要があるため、一度だけ開くことができます)。
ジュール

そして答えで述べたように、closeメソッドが利用可能な場合(またはリソースをusingブロックで使用できる場合)、これらは正しいアプローチです。答えは、これらのメカニズムが利用できないまれなケースを特に扱います。
James_pic

私個人の意見では、メモリ以外のリソースを管理しているが、closeメソッドを提供しないインターフェイスは、使用すべきではないインターフェイスです。信頼性の高い方法はありません
ジュール

@Jules同意しますが、時には避けられないことがあります。時には抽象化が漏れる場合があり、漏れのある抽象化を使用することは、抽象化を使用しないよりも優れています。時には、守れないと約束することを要求するレガシーコードを使用する必要があります。はい、それはまれであり、可能な場合は回避する必要があり、ガベージコレクションを強制することについてこれらの警告がすべてある理由がありますが、これらの状況が発生し、OPはこれらの状況がどのように見えるかを尋ねていました-私は答えました。
James_pic

2

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

ありますいくつかのケースではときあなたはその後、ガベージコレクタが行うメモリ使用量についての詳細を知っています。これは、マルチユーザーアプリケーション、または一度に複数の要求に応答するサービスでは当てはまりません。

ただし、一部のバッチタイプの処理では、GCの詳細を知っています。たとえば、アプリケーションを考えます。

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

あなたはありますが、それぞれのファイルを処理した後で、完全なガベージコレクションを強制する必要があり、試験(慎重後)のケースを作ることができます。

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

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

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

リコマリアーニのパフォーマンスに関するヒント」も参照してください。


2

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

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

1
誰かが下票の理由を指摘できますか?私自身は、答えを判断するのに十分な知識がありません(一見すると、私にとっては理にかなっています)。
オメガ

1
私はあなたが3番目の点について下票を得たと推測しています。「これは単なる常識だ」と言う可能性もあります。
イミビス

2
オブジェクトの大きなコレクションを作成するとき、GCはコレクションが必要かどうかを知るのに十分スマートでなければなりません。メモリを圧縮する必要がある場合も同じです。関連するオブジェクトのメモリの局所性を最適化するためにGCに依存することは現実的ではないようです。他の解決策(構造、安全でない、...)を見つけることができると思います。(私はダウンボッターではありません)。
ギヨーム

3
OKの時間についてのあなたの最初のアイデアは、私の意見では単に悪いアドバイスです。最近コレクションが収集されている可能性が高いので、再度収集しようとすると、単にオブジェクトを後の世代に勝手に昇格させようとしますが、これはほとんど常に悪いことです。後の世代には、最初から時間がかかるコレクションがあり、ヒープサイズを大きくすると「できるだけ多くのスペースを空ける」ため、これが問題になります。プラスあなたは、負荷とメモリの圧力を増加しようとしているならば、あなたはとにかくコレクションを誘発開始する可能性が高いです、Gen1の/ 2増加するので、もっとゆっくり実行する
ジミー・ホッファ

2
By calling gc() depending on implementation, the memory in used will be compacted which massively improves cache locality. This will result in massive improve in performance that you will not get from profiling.大量のオブジェクトを連続して割り当てると、それらはすでに圧縮されています。どちらかといえば、ガベージコレクションはそれらを少しシャッフルする可能性があります。いずれにせよ、高密度でメモリ内をランダムに飛び回らないデータ構造を使用すると、より大きな影響があります。単純なノードあたり1要素のリンクリストを使用している場合、手動のGCトリックはそれを補いません。
ドーバル

2

実際の例:

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

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

しかし、非常に大きいため、単純なロードでは、将来的に成長するため、データとともに少なくとも6GBのメモリを占有します。(正確な数値はありません。2GBのマシンが少なくとも6GBに対応しようとしていたことが明らかになれば、動作しないことを知るために必要なすべての測定値が得られました)。

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

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

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

この実際のケースは、いつ使用すべきかのガイドラインの良い例ですGC.Collect()

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

ほとんどの場合、GC.Collect()ポイント1と2が適用され、ポイント3は事態を悪化させるか、少なくとも事態を改善しなかった(そして改善がほとんどまたはまったくなかったため)アプローチがアプリケーションの存続期間中により良くなる可能性が高いため、コールオーバーではなくコールアウトに傾く)。


0

私は、ちょっと変わったごみ処理の使用法を持っています。

IDisposable-disposingとして知られる、ugい、不格好な、上品で、エラーが発生しやすいイディオムを使用してオブジェクトの破棄を実装する、C#の世界では残念ながら非常に普及しているこの誤った方法があります。MSDN はそれを長さ説明し、多くの人々がそれを誓い、宗教に従って、何時間もかけて正確にどのようにすべきかなどを議論します。

(ここで私がlyいと呼んでいるのはオブジェクト廃棄パターンそのものではないことに注意してください;私がlyいと呼んでいるのは特定のIDisposable.Dispose( bool disposing )イディオムです。)

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

しかし、その後、IDisposable.Dispose()クリーンアップする管理対象オブジェクトと管理対象外オブジェクトの両方がありますが、管理対象オブジェクトIDisposable.Dispose()はデストラクタ内から呼び出されたときにクリーンアップできません。その時点で既にガベージコレクタによって処理されているためです。これは、管理対象オブジェクトと管理対象外オブジェクトの両方をクリーンアップするか、管理対象外オブジェクトのみをクリーンアップするかを知るDispose()ためのbool disposingフラグを受け入れる別のメソッドが必要なことです。

すみませんが、これは非常識です。

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

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

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

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


Now, strictly speaking, it is of course impossible to guarantee that no programmer will ever make the mistake of forgetting to invoke IDisposable.Dispose()実際には、そうではありませんが、C#には対応していないと思います。リソースを公開しないでください。代わりに、あなたがそれでやるすべて(基本的にはモナド)を記述するためのDSLに加えて、リソースを取得し、物事を行い、それを解放し、結果を返す関数を提供します。トリックは、型システムを使用して、誰かがリソースへの参照を密輸しても、run関数への別の呼び出しで使用できないことを保証することです。
ドーバル

2
Dispose(bool disposing)(定義されていない)の問題はIDisposable、オブジェクトがフィールドとして持っている管理対象オブジェクトと非管理対象オブジェクトの両方のクリーンアップを処理するために使用される(またはそれ以外の責任がある)ことで、間違った問題を解決します。心配する他の使い捨てオブジェクトのない管理対象オブジェクト内の管理対象外オブジェクトは、すべてのDispose()メソッドがそれらのいずれか(必要に応じてファイナライザに同じクリーンアップを行わせる)または管理対象オブジェクトのみを破棄する(ファイナライザを持たない)すべての)、および必要性bool disposingが消滅。
ジョンハンナ

-1ファイナライズが実際に機能するため、悪いアドバイス。私は全くのあなたのポイントに同意するdispose(disposing)イディオムいるterribad、彼らは唯一の(リソースを管理しているとき、人々はそう頻繁にその技術とファイナライザを使用するので、私は、このような言いDbConnectionインスタンスのオブジェクトがされて管理さ、それはpinvokedていないか、COMが整列化、)、およびYOUはONLY SHOULDファイナライザーを管理されていない、暗示された、COMマーシャルされた、または安全でないコードで実装してください。私は、高価なファイナライザがあるかはなはだ私の答えでは上に詳述していない、あなたが持っていない限り、それらを使用管理されていない、あなたのクラスのリソースを。
ジミー・ホッファ

2
あなたが非常に多くの人々がdispose(dispoing)イディオムの中核となる重要なものとして取る何かを非難しているという理由だけで、私はあなたに+1を与えたいと思っていますが、真実は、人々がGCのようなものをとても恐れているので、それは非常に一般的ですそれは(disposeGCと関係のあるジルチを持っているべきです)それを調査することさえせずに処方された薬を服用するだけの価値があります。それを検査するのにあなたに良いが、あなたは最大の全体を見逃した(それはファイナライザを彼らがあるべきよりもはるかに頻繁に奨励する)
ジミー・

1
@JimmyHoffa、ご意見ありがとうございます。通常、ファイナライザーはアンマネージリソースの解放にのみ使用することに同意しますが、DEBUGビルドではこのルールは適用できず、DEBUGビルドではバグをキャッチするためにファイナライザーを自由に使用できることに同意しませんか?私がここで提案しているのはそれだけですので、なぜあなたがそれを問題にするのか分かりません。Java側のこのアプローチの詳細な説明については、programmers.stackexchange.com / questions / 288715 /… も参照してください。
マイクナキス

0

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

非常に理論的に言えば、いくつかのGC実装の​​ような問題を無視してコレクションサイクル中に物事を遅くします。予想外の時間に、人間の命やこの種の何かを犠牲にするかもしれません。

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

ゲームの場合、クラッシュは迅速に検出および修正できるという意味で望ましい場合がありますが、ミッションクリティカルなプログラムの場合、まったく予期しないタイミングでクラッシュすると誰かが死亡する可能性があります。だから、私が思う主なケースは、クラッシュしないか、他のフォームがセキュリティであるというシナリオが絶対に重要であり、論理的リークは比較的些細なことだと思います。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.