すべての 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.SuppressFinalize
Dispose()
メソッドでは一般的です)、オブジェクトの固定を解除したのではないかと疑っていますが、それについては引用しないでください。このオブジェクトがどのヒープにある場合でも、次のコレクションが最終的に収集します。Gen 0コレクションは、ファイナライズが必要なビットをオンにしたオブジェクトにも注意を払わず、ルートを確認することなく自動的にそれらを昇格させます。Gen 1でファイナライズを必要とするルート化されていないオブジェクトはFReachable
キューに放り込まれますが、コレクションはそれで何もしないため、Gen 2に残ります。このように、ファイナライザーを持ち、GC.SuppressFinalize
Gen 2で収集されます。