9

私のアプリケーションでは、多数の大きなオブジェクトがすべて一度に解放される特定の時間があります。その際、特にラージ オブジェクト ヒープ (LOH) でガベージ コレクションを実行したいと考えています。

GC.Collect(2)ジェネレーション 2 コレクションを実行しているときに GC が LOH でのみ呼び出されるため、呼び出す必要があります。GC.Collect(2)ただし、ジェネレーション 1 と 0で呼び出すと GC が引き続き実行されるというドキュメントを読みました。

GC にgen 2のみを収集させ、gen 1 または gen 0 を含めないようにすることは可能ですか?

それが不可能な場合、GC をそのように設計する理由はありますか?

4

4 に答える 4

14

不可能です。GC は、ジェネレーション 2 のコレクションが常にジェネレーション 0 と 1 も収集するように設計されています。

編集: GC開発者のブログでこれのソースを見つけました:

Gen2 GC には完全なコレクション (Gen0、Gen1、Gen2、および LOH) が必要です。LOH のスペース不足によって GC がトリガーされなかった場合でも、Gen2 GC ごとに大きなオブジェクトが GC されます。これは、若い世代のコレクションよりもはるかに時間がかかります。

編集2:同じブログのUsing GC Efficiently Part 1Part 2から、明らかにGen0およびGen1コレクションはGen2コレクションと比較して高速であるため、Gen2のみを実行してもパフォーマンスが大幅に向上しないことは合理的です。もっと根本的な理由があるのか​​もしれませんが、私にはわかりません。その答えは、そのブログのどこかの記事にあるかもしれません。

于 2009-09-23T22:25:09.450 に答える
6

すべての新しい割り当て (ラージ オブジェクトを除く)は常にGen0 で行われるため、GC は指定された世代以下から常に収集するように設計されています。を呼び出すとGC.Collect(2)、GC に Gen0、Gen1、および Gen2 から収集するように指示されます。

多くの大きなオブジェクト (割り当て時に LOH に配置するのに十分な大きさのオブジェクト) を扱っていることが確実な場合は、完了時にそれらを null (VB では Nothing) に設定することをお勧めします。彼らと。LOH 割り当ては、スマートでブロックを再利用しようとします。たとえば、LOH に 1MB のオブジェクトを割り当て、それを破棄して null に設定すると、1MB の「穴」が残ります。次に LOH に 1MB 以下のサイズのものを割り当てる、その穴が埋められます (そして、次の割り当てが大きすぎて残りのスペースに収まらないまで穴を埋め続けます。新しいブロック。)

.NET の世代は物理的なものではなく、GC のパフォーマンスを向上させるための論理的な分離であることに注意してください。新しい割り当てはすべて Gen0 に行われるため、常に最初の世代が収集されます。実行されるコレクション サイクルごとに、下位世代でコレクションを生き残ったものはすべて、次に上位の世代に "昇格" されます (Gen2 に到達するまで)。

ほとんどの場合、GC は Gen0 の収集を超える必要はありません。GC の現在の実装では、Gen0 と Gen1 を同時に収集できますが、Gen0 または Gen1 の収集中に Gen2 を収集することはできません。(.NET 4.0 ではこの制約が大幅に緩和され、ほとんどの場合、GC は Gen0 または Gen1 も収集されている間に Gen2 を収集できます。)

于 2009-09-23T22:58:04.190 に答える
0

システムが特定の世代のガベージ コレクションを実行するときはいつでも、その世代の任意のオブジェクトへの参照を保持している可能性のあるすべてのオブジェクトを調べる必要があります。多くの場合、古いオブジェクトは他の古いオブジェクトへの参照のみを保持します。システムが Gen0 コレクションを実行している場合、Gen1 および/または Gen2 のオブジェクトへの参照のみを保持するオブジェクトは無視できます。同様に、Gen1 コレクションを実行している場合、Gen2 への参照のみを保持するオブジェクトは無視できます。オブジェクトの調査とタグ付けは、ガベージ コレクションに必要な時間の大部分を占めるため、古いオブジェクトを完全にスキップできると、かなりの時間を節約できます。

ちなみに、オブジェクトが新しいオブジェクトへの参照を保持している可能性があるかどうかをシステムがどのように「認識」しているか疑問に思っている場合、システムには、オブジェクトが書き込まれた場合に各オブジェクトの記述子にいくつかのビットを設定する特別なコードがあります。最初のビットは各ガベージ コレクションでリセットされ、次のガベージ コレクションでまだリセットされている場合、システムは Gen0 オブジェクトへの参照を含めることができないことを認識します (オブジェクトが最後に書き込まれたときに存在し、存在しなかったオブジェクトがあるため)前のコレクションでクリアされたものは、Gen1 または Gen2 になります)。2 番目のビットは各 Gen1 ガベージ コレクションでリセットされ、次の Gen1 ガベージ コレクションでまだリセットされている場合、システムは Gen0 または Gen1 オブジェクトへの参照を含めることができないことを認識します (参照を保持しているオブジェクトは現在 Gen2 です)。 . システムがそうではないことに注意してください。オブジェクトに書き込まれた情報に Gen0 または Gen1 参照が含まれているかどうかはわかりません。タグなしオブジェクトへの書き込み時に必要なトラップはコストが高く、オブジェクトが書き込まれるたびにトラップを処理する必要がある場合、パフォーマンスが大幅に低下します。これを避けるために、オブジェクトは常にタグ付けされます。任意の書き込みが発生するため、次のガベージ コレクションの前に追加の書き込みを中断することなく続行できます。

于 2011-06-24T15:06:21.537 に答える
0

「なぜ」という質問に答えるには、物理​​的に、Gen0 や Gen1、Gen2 などは存在しません。それらはすべて、仮想アドレス空間で同じメモリ ブロックを使用します。それらの間の区別は、実際には、仮想的な境界制限を移動することによってのみ事実上行われます.

すべての (小さい) オブジェクトは Gen0 ヒープ領域から割り当てられます。コレクションの後、生き残った場合、マネージド ヒープ ブロックのその領域に「下方に」移動され、最終的にガベージから解放されます。これは、ヒープを圧縮することによって行われます。完全なコレクションが終了した後、Gen1 の新しい「境界」は、生き残ったオブジェクトの直後のスペースに設定されます。

そのため、外に出て Gen0 や Gen1 をクリアしようとすると、ヒープに穴が開いてしまい、「完全な」ヒープ (Gen0 のオブジェクトであっても) を圧縮して閉じる必要があります。いずれにせよ、これらのオブジェクトのほとんどはガベージになるため、明らかにこれは意味がありません。それらを移動しても意味がありません。また、(そうでなければ圧縮する)ヒープに大きな穴を作成して残すことには意味がありません。

于 2011-01-27T08:41:00.740 に答える