webdevqa.jp.net

GC.Collect()の使用の何がそんなに悪いのですか?

私はこの機能で遊ぶことの重大な意味を理解していますが(または少なくともそれは私が思うことです)、立派なプログラマーが決して使用しないであろうこれらのことの1つになっている理由を理解できません。それは何のためです。

ユーザーが何をしているかによってメモリ使用量が極端に変化するアプリケーションを開発しているとしましょう。アプリケーションのライフサイクルは、編集とリアルタイム処理の2つの主要な段階に分けることができます。編集段階で、数十億または数兆ものオブジェクトが作成されたとします。それらのいくつかは小さく、いくつかはそうではなく、いくつかはファイナライザを持っているかもしれませんし、いくつかは持っていないかもしれません。次に、ユーザーはリアルタイムステージに切り替えることにします。この時点で、パフォーマンスが基本的な役割を果たし、プログラムのフローのわずかな変更が壊滅的な結果をもたらすと仮定します。次に、オブジェクトプールなどを使用してオブジェクトの作成を最小限に抑えますが、その後、GCが予期せずに鳴り響き、すべてが破棄され、誰かが死にます。

質問:この場合、第2段階に入る前にGC.Collect()を呼び出すのが賢明ではないでしょうか?

結局のところ、これらの2つの段階は互いに時間的に重複することはなく、GCが収集する可能性のあるすべての最適化と統計はここではほとんど役に立ちません...

注:一部の人が指摘したように、.NETはこのようなアプリケーションに最適なプラットフォームではないかもしれませんが、それはこの質問の範囲を超えています。その目的は、GC.Collect()呼び出しがアプリケーションの全体的な動作/パフォーマンスを改善できるかどうかを明確にすることです。このようなことを行う状況は非常にまれですが、GCはほとんどの場合、推測しようとし、完全にそれを実行しようとしますが、それでも推測についてです。

ありがとう。

102
Trap

リコのブログから...

ルール#1

しないでください。

これが本当に最も重要なルールです。 GC.Collect()のほとんどの使用法は悪い考えであると言うのは公正であり、オリジナルの投稿で詳細に説明したので、ここではすべてを繰り返しません。それでは、次に進みましょう...

ルール#2

非定期的なイベントが発生したばかりで、このイベントが多くの古いオブジェクトを消滅させる可能性が高い場合は、GC.Collect()を呼び出すことを検討してください。

これの典型的な例は、クライアントアプリケーションを作成していて、多くのデータが関連付けられている非常に大きく複雑なフォームを表示する場合です。ユーザーはこのフォームと対話しただけで、XMLドキュメントや大きなDataSetなどの大きなオブジェクトを作成する可能性があります。フォームを閉じると、これらのオブジェクトは無効になり、GC.Collect()はそれらに関連付けられたメモリを回収します...

したがって、この状況はルール#2に該当するように思えますが、多くの古いオブジェクトが消滅した瞬間があり、それは繰り返し発生しないことがわかっています。しかし、リコの別れの言葉を忘れないでください。

ルール#1は、強力な証拠なしにルール#2に勝るべきです。

測定、測定、測定。

87
Jon Norton

実稼働コードでGC.Collect()を呼び出す場合、本質的には、GCの作成者よりも多くのことを知っていると宣言しています。そうかもしれません。ただし、通常はそうではないため、お勧めできません。

56
Aaron Fischer

では、MS Wordや.NETのMS ExcelなどのCOMオブジェクトを使用する場合はどうでしょうか。 COMオブジェクトのリリース後にGC.Collectを呼び出さずに、WordまたはExcelアプリケーションのインスタンスがまだ存在することがわかりました。

実際、使用するコードは次のとおりです。

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.Microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

それはガベージコレクターの誤った使用でしょうか?もしそうなら、どうすれば相互運用オブジェクトを消滅させることができますか?また、このように使用することを意図していない場合、なぜGCCollectメソッドはPublicでさえあるのでしょうか。

23
Dib

さて、GCは私が愛/憎しみの関係を持っているものの1つです。 VistaDBを通じて 過去に壊れた があり、それについてブログに書きました。彼らはそれを修正しましたが、このようなものについて修正を得るには長い時間がかかります。

GCは複雑で、1つのサイズですべてのアプローチに適合するので、これほど大きなものを実現するのは非常に困難です。 MSはかなり良い仕事をしましたが、GCをだますことは時々可能です。

一般に、大量のメモリをダンプしたばかりで、GCがクリーンにならない場合は 人生の危機 に移動することがわかっている場合を除き、Collectを追加しないでください。今すぐ。

一連の不正なGC.Collectステートメントでマシン全体を台無しにすることができます。 collectステートメントの必要性は、ほとんどの場合、より大きな根本的なエラーを示しています。メモリリークは通常、参照とそれらがどのように機能するかを理解していないことに関係しています。または、それを必要としないオブジェクトでIDisposableを使用し、GCに非常に高い負荷をかけます。

システムパフォーマンスカウンターを使用して、GCに費やした時間の%を注意深く監視します。 GCで20%以上の時間をアプリが使用している場合、オブジェクト管理に深刻な問題(または異常な使用パターン)があります。 GCが費やす時間は、アプリ全体を高速化するため、常に最小限に抑える必要があります。

また、GCはワークステーションとサーバーでは異なることに注意することも重要です。私は、両方をテストしていない(またはそれらが2つあることを認識していない)人々を追跡するのが難しいいくつかの小さな問題を見てきました。

そして、私の答えをできるだけ一杯にするために、そのプラットフォームをターゲットにしている場合もMonoでテストする必要があります。まったく異なる実装であるため、MS実装とはまったく異なる問題が発生する可能性があります。

15
Jason Short

役に立つ状況もありますが、一般的には避けるべきです。 GOTOと比較したり、モペットに乗ったりすることができます。必要なときにそれを行いますが、友達にそれを伝えません。

13
rjohnston

私の経験から、実稼働コードでGC.Collect()を呼び出すことはお勧めできませんでした。デバッグでは、はい、潜在的なメモリリークを明らかにするのに役立つという利点があります。私の基本的な理由は、GCが私よりもはるかに賢いプログラマによって書かれ最適化されていることであり、GC.Collect()を呼び出す必要があると感じた場合どこかに。あなたの状況では、実際にメモリの問題があるようには聞こえませんが、コレクションがプロセスにどのような不安定さをもたらすかを心配しているだけです。まだ使用中のオブジェクトを消去せず、需要の増加と減少の両方に非常に迅速に適応するので、心配する必要はないと思います。

12
TheZenker

GC.Collect()を呼び出す最大の理由の1つは、記述したような大量のゴミを作成する重要なイベントを実行したときです。ここでは、GC.Collect()を呼び出すことをお勧めします。そうしないと、GCは、それが「一度限り」のイベントであったことを理解しない場合があります。

もちろん、プロファイルを作成して、自分で確認する必要があります。

10
TraumaPony

もちろん、非リアルタイムガベージコレクションを使用する言語では、リアルタイム要件を備えたコードを作成しないでください。

ステージが明確に定義されている場合、ガベージコレクターをトリガーしても問題はありません。しかし、このケースは非常にまれです。問題は、多くの開発者がこれをカーゴカルトスタイルでペーパーオーバーの問題に使用しようとすることであり、無差別に追加するとパフォーマンスの問題が発生することです。

9
wnoise

GC.Collect()を呼び出すと、CLRが強制的にスタックウォークを実行し、参照を確認することで各オブジェクトを本当に解放できるかどうかを確認します。これは、オブジェクトの数が多い場合にスケーラビリティに影響し、ガベージコレクションを頻繁にトリガーすることもわかっています。 CLRを信頼し、必要に応じてガベージコレクターを実行させます。

7
kd7

ループ内のイメージの作成-disposeを呼び出しても、メモリは回復しません。ごみは毎回収集します。写真処理アプリの1.7GBのメモリから24MBに変更しましたが、パフォーマンスは優れています。

GC.Collectを呼び出す必要がある時間は絶対にあります。

6
dale

実際、GC.Collectを呼び出すことは非常に悪い習慣ではないと思います。
必要な場合があります。たとえば、スレッドを実行し、データベース内の異なるテーブルを開き、BLOBフィールドの内容を一時ファイルに抽出し、ファイルを暗号化してから、バイナリストリームに読み込んでBLOBに戻すフォームがあります別のテーブルのフィールド。

操作全体には非常に多くのメモリが必要であり、テーブル内の行数とファイルコンテンツのサイズについては定かではありません。

OutofMemory Exceptionを頻繁に取得していましたが、カウンター変数に基づいてGC.Collectを定期的に実行するのが賢明だと思いました。カウンターをインクリメントし、指定されたレベルに達すると、GCが呼び出されて、形成された可能性のあるガベージを収集し、予期しないメモリリークによって失われたメモリを回収します。

この後、私はそれがうまく機能していると思います、少なくとも例外はありません!!!
次の方法で電話します。

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).
6
Venugopal M

.netでは、ガベージコレクションを実行するのに必要な時間は、ガベージコレクションではなく、ガベージコレクションではなく、ガベージコレクションではありません。実際、オブジェクトがFinalizeをオーバーライドしない限り(明示的に、またはC#デストラクタを介して)、WeakReferenceのターゲットであるか、ラージオブジェクトヒープ上にあるか、または他のgc関連の特殊な場合方法として、オブジェクトとして存在するメモリを識別する唯一のものは、そのオブジェクトへのルート化された参照の存在です。それ以外の場合、GCの操作は、価値のあるものすべてを建物から取り出し、建物を破壊し、古い建物の敷地に新しい建物を建て、そこにすべての貴重なアイテムを置くことに似ています。建物をダイナマイトするために必要な努力は、その中のゴミの量とは完全に独立しています。

したがって、GC.Collectを呼び出すと、システムが実行しなければならない作業全体の量が増加する傾向があります。次のコレクションの発生を遅らせますが、おそらく次のコレクションが発生したときに必要だったのと同じくらいすぐに作業を行います。次の収集が行われる時点で、収集に費やされた合計時間はGC.Collectが呼び出されなかったのとほぼ同じになりますが、システムはガベージを蓄積し、後続の収集が行われます。 GC.Collectが呼び出されなかったよりも早く必要です。

GC.Collectが本当に役立つのは、コードのメモリ使用量を測定する必要がある場合(メモリ使用量の数値はコレクションの後にのみ意味があるため)、またはいくつかのアルゴリズムのどちらが優れているかをプロファイルする必要がある場合です(呼び出しいくつかのコードを実行する前にGC.Collect()を使用すると、一貫したベースライン状態を確保できます)。 GCが知らないことを知っているかもしれない他のいくつかのケースがありますが、シングルスレッドのプログラムを書いていない限り、1つのスレッドのデータ構造を避けるのに役立つGC.Collect呼び出しを知る方法はありません「中世の危機」によって、他のスレッドのデータが「中年の危機」を引き起こすことはありません。

5
supercat

コレクションを明示的に呼び出すことに何も問題はありません。一部の人々は、それがベンダーによって提供されるサービスである場合、それを疑ってはいけないと本当に信じたいだけです。ああ、あなたのインタラクティブなアプリケーションの間違った瞬間にこれらのランダムフリーズがすべてありますか?次のバージョンでは改善されます!

バックグラウンドプロセスにメモリ操作を処理させることは、それを自分で処理する必要がないことを意味します。しかし、これは論理的にすべての状況下で自分で対処しないことが最善であることを意味しません。 GCはほとんどの場合に最適化されています。しかし、これは論理的にすべての場合に最適化されることを意味しません。

「これが最良のソートアルゴリズムである」などの未解決の質問に決定的な答えを出したことがありますか?その場合、GCに触れないでください。条件を求めた人、または「この場合」タイプの回答をした人のために、GCとそれをいつアクティブにするかについて学ぶことができます。

おっと、私はChromeでアプリケーションがフリーズし、Firefoxが私を地獄に追い込んでしまいました。ガベージコレクター-または、ページのテキストの読み取りを開始するときにボタンを押して、次の20分間はフリーズを解除できるようにします。

4
Gerard ONeill

ガベージコレクタがガベージを収集せず、メモリを解放しないという同様の問題がありました。

私たちのプログラムでは、いくつかの適度なサイズのExcelスプレッドシートをOpenXMLで処理していました。スプレッドシートには、約1000行の14列の5〜10個の「シート」が含まれていました。

32ビット環境(x86)のプログラムは、「メモリ不足」エラーでクラッシュします。 x64環境で実行することはできましたが、より良いソリューションが必要でした。

見つけました。

ガベージコレクターを明示的に呼び出して破棄されたオブジェクトからメモリを解放する場合に、機能しなかったものと機能したものの簡単なコードフラグメントを次に示します。

サブルーチン内からGCを呼び出しても機能しませんでした。メモリーが再生されることはありませんでした...

_For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub
_

GC呼び出しをサブルーチンのスコープ外に移動することにより、ガベージが収集され、メモリが解放されました。

_For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub
_

GC.Collect()への呼び出しを無視するように見えるとき、これが.NETガベージコレクションにイライラしている他の人を助けることを願っています。

ポールスミス

4
Paul Smith

シナリオについては正しいと思いますが、APIについてはわかりません。

マイクロソフトは、そのような場合には、コレクションをすぐに実行する必要があるというGCへのヒントとして、 メモリプレッシャーを追加 する必要があると述べています。

2
Sergio Acosta

GC.Collect()を呼び出したいという欲求は、通常、他のどこかで犯した間違いを隠そうとしています。

不要になったものを処分するのを忘れた場所を見つけたらもっといいでしょう。

2
Sam

どうしたの?ガベージコレクターとメモリアロケーターを再推測しているという事実。これらは、実行時におけるアプリケーションの実際のメモリ使用量について、あなたよりもはるかに優れた考えを持っています。

2
Rob

要するに、アプリケーションのプロファイルを作成して、これらの追加コレクションが物事にどのように影響するかを確認できます。あなたがプロファイルするつもりでない限り、私はそれから離れることをお勧めします。 GCは、それ自体を処理するように設計されており、ランタイムが進化するにつれて、効率が向上する場合があります。たくさんのコードがぶらぶらしていて、作品が台無しになり、これらの改善点を利用できなくなるのは望ましくありません。 forの代わりにforeachを使用することについても同様の議論があります。つまり、カバーの下にある将来の改善をforeachに追加でき、コードを利用して変更する必要がないということです。

1

.NET Framework自体は、リアルタイム環境で実行するようには設計されていません。リアルタイム処理が本当に必要な場合は、.NETに基づいていない埋め込みリアルタイム言語を使用するか、Windows CEデバイスで実行される.NET Compact Frameworkを使用します。

1
Scott Dorman

最悪の事態は、プログラムを少しの間フリーズさせることです。よろしければそれをしてください。通常、ユーザーとの対話を主に行うシッククライアントまたはWebアプリには必要ありません。

オブジェクトを適切に破棄している場合でも、実行時間が長いスレッドを含むプログラム、またはバッチプログラムがOutOfMemory例外を取得することがあります。私が思い出したのは、基幹業務データベースのトランザクション処理でした。もう1つは、シッククライアントアプリのバックグラウンドスレッドのインデックス作成ルーチンでした。

どちらの場合も、結果は単純でした。GC.Collectは、メモリ不足ではありません。 GC.Collect、完璧なパフォーマンス。

私はそれを何回かメモリ問題を解決するために試しましたが、役に立ちませんでした。取り出した。

要するに、エラーが発生しない限り、入れないでください。挿入してもメモリの問題が解決しない場合は、取り出してください。リリースモードでテストし、リンゴとリンゴを比較することを忘れないでください。

これがうまくいかないのは、それについて道徳的になるときだけです。値の問題ではありません。多くのプログラマーが亡くなり、多くの不必要なGCを収集して天国に直行しました。

1
FastAl