20

.NET ガベージ コレクターは、オブジェクトを収集し (それらのメモリを再利用します)、メモリの圧縮も実行します (メモリの断片化を最小限に抑えるため)。

アプリケーションにはオブジェクトへの参照が多数ある可能性があるため、GC による圧縮によりオブジェクトのアドレスが変更された場合、GC (または CLR) はこれらのオブジェクトへの参照をどのように管理するのでしょうか。

4

3 に答える 3

14

概念は非常に単純です。ガベージコレクターは、オブジェクト参照を更新し、移動されたオブジェクトを再ポイントするだけです。

実装は少しトリッキーです。ネイティブコードとマネージコードの間に実際の違いはありません。どちらもマシンコードです。また、オブジェクト参照について特別なことは何もありません。実行時の単なるポインタです。必要なのは、コレクターがこれらのポインターを見つけて、管理対象オブジェクトを参照する種類としてそれらを認識するための信頼できる方法です。圧縮中にポイントされたオブジェクトが移動されたときにそれらを更新するだけでなく、オブジェクトがすぐに収集されないようにするライブ参照を認識します。

これは、GCヒープに格納されているクラスオブジェクトに格納されているオブジェクト参照の場合は簡単です。CLRは、オブジェクトのレイアウトと、ポインタを格納しているフィールドを認識しています。スタックまたはCPUレジスタに格納されているオブジェクト参照の場合はそれほど単純ではありません。ローカル変数やメソッド引数のように。

ネイティブコードとは異なるマネージコードを実行することの重要な特性は、CLRがマネージコードが所有するスタックフレームを確実に反復できることです。スタックフレームのセットアップに使用されるコードの種類を制限することによって行われます。これは通常、ネイティブコードでは不可能であり、「フレームポインタの省略」最適化オプションは特に厄介です。

スタックフレームウォーキングでは、まず、スタックに格納されているオブジェクト参照を検索できます。また、スレッドが現在マネージコードを実行していることを通知するため、CPUレジスターも参照をチェックする必要があります。マネージコードからネイティブコードへの移行には、コレクターが認識するスタックに特別な「Cookie」を書き込むことが含まれます。したがって、後続のスタックフレームには、管理対象オブジェクトを参照しないランダムなポインタ値が含まれるため、チェックしないでください。

アンマネージコードのデバッグを有効にすると、デバッガーでこれを確認できます。[コールスタック]ウィンドウを見て、[ネイティブからマネージドへの移行]と[マネージドからネイティブへの移行]のアノテーションに注意してください。これが、これらのCookieを認識するデバッガーです。[ローカル]ウィンドウに意味のあるものを表示できるかどうかを知る必要があるため、これも重要です。スタックウォークはフレームワークでも公開されています。StackTraceクラスとStackFrameクラスに注意してください。また、サンドボックス化には非常に重要です。CodeAccess Security(CAS)はスタックウォークを実行します。

于 2012-05-20T14:49:59.043 に答える
8

簡単にするために、オブジェクトが固定されておらず、GC サイクルごとにすべてのオブジェクトがスキャンされて再配置され、送信先がソースと重複しないストップ・ザ・ワールド GC を想定します。実際には、.NET GC はもう少し複雑ですが、これで物事がどのように機能するかがよくわかるはずです。

参照が検査されるたびに、次の 3 つの可能性があります。

  1. ヌルです。その場合、処置は必要ありません。

  2. これは、再配置マーカー (以下で説明する特別な種類のオブジェクト) 以外のものであるとヘッダーに示されているオブジェクトを識別します。その場合、オブジェクトを新しい場所に移動し、元のオブジェクトを、新しい場所、現在のオブジェクトへのちょうど観測された参照を含むオブジェクトの古い場所、および内部のオフセットを含む 3 語の再配置マーカーで置き換えます。そのオブジェクト。次に、新しいオブジェクトのスキャンを開始します (アドレスを記録したばかりなので、システムはその時点でスキャンされていたオブジェクトを忘れることができます)。

  3. これは、再配置マーカーであるとヘッダーに示されているオブジェクトを識別します。その場合は、スキャン中の参照を更新して、新しいアドレスを反映させます。

システムが現在のオブジェクトのスキャンを終了すると、以前の場所を調べて、現在のオブジェクトのスキャンを開始する前に何をしていたかを調べることができます。

オブジェクトが再配置されると、最初の 3 ワードの以前の内容が新しい場所で利用可能になり、古い場所では必要なくなります。オブジェクトへのオフセットは常に 4 の倍数であり、個々のオブジェクトはそれぞれ 2GB に制限されているため、可能なすべてのオフセットを保持するには、可能なすべての 32 ビット値の一部のみが必要になります。オブジェクトのヘッダー内の少なくとも 1 つの単語に、オブジェクト再配置マーカー以外には保持できない少なくとも 2^29 の値があり、すべてのオブジェクトに少なくとも 12 バイトが割り当てられている場合、オブジェクト スキャンで任意のオブジェクトを処理できます。コンテンツが不要になったオブジェクトの古いコピーが占有する領域の外側に、深さに依存するストレージを必要とせずに、ツリーの深さを拡張します。

于 2015-03-16T18:58:40.167 に答える
4

ガベージコレクション

すべてのアプリケーションには一連のルートがあります。ルートは、管理対象ヒープ上のオブジェクトまたはnullに設定されているオブジェクトを参照するストレージの場所を識別します。たとえば、アプリケーション内のすべてのグローバルおよび静的オブジェクトポインターは、アプリケーションのルートの一部と見なされます。さらに、スレッドのスタック上のローカル変数/パラメーターオブジェクトポインターは、アプリケーションのルートの一部と見なされます。最後に、マネージヒープ内のオブジェクトへのポインタを含むCPUレジスタも、アプリケーションのルートの一部と見なされます。アクティブなルートのリストは、ジャストインタイム(JIT)コンパイラーと共通言語ランタイムによって維持され、ガベージコレクターのアルゴリズムにアクセスできるようになります。

ガベージコレクターが実行を開始すると、ヒープ内のすべてのオブジェクトがガベージであると想定されます。つまり、アプリケーションのルートがヒープ内のオブジェクトを参照していないことを前提としています。これで、ガベージコレクターはルートをウォークし、ルートから到達可能なすべてのオブジェクトのグラフを作成し始めます。たとえば、ガベージコレクターは、ヒープ内のオブジェクトを指すグローバル変数を見つけることができます。

グラフのこの部分が完了すると、ガベージコレクターは次のルートをチェックし、オブジェクトを再度ウォークします。ガベージコレクターがオブジェクトからオブジェクトへと移動するときに、以前に追加したグラフにオブジェクトを追加しようとすると、ガベージコレクターはそのパスをたどるのをやめることができます。これには2つの目的があります。まず、オブジェクトのセットを2回以上ウォークスルーしないため、パフォーマンスが大幅に向上します。次に、オブジェクトの循環リンクリストがある場合に無限ループを防ぎます。

すべてのルートがチェックされると、ガベージコレクターのグラフには、アプリケーションのルートから何らかの方法で到達可能なすべてのオブジェクトのセットが含まれます。グラフにないオブジェクトはアプリケーションからアクセスできないため、ガベージと見なされます。ガベージコレクターは、ヒープを直線的にウォークスルーし、ガベージオブジェクトの連続したブロック(現在は空き領域と見なされます)を探します。次に、ガベージコレクターは、ガベージ以外のオブジェクトをメモリ内でシフトダウンし(長年使用されている標準のmemcpy関数を使用して)、ヒープ内のすべてのギャップを削除します。もちろん、メモリ内のオブジェクトを移動すると、オブジェクトへのすべてのポインタが無効になります。したがって、ガベージコレクターは、ポインターがオブジェクトの新しい場所を指すように、アプリケーションのルートを変更する必要があります。さらに、いずれかのオブジェクトに別のオブジェクトへのポインターが含まれている場合、ガベージコレクターはこれらのポインターも修正する責任があります。

C#の固定ステートメント

固定ステートメントは、管理対象変数へのポインターを設定し、ステートメントの実行中にその変数を「固定」します。固定されていないと、ガベージコレクションによって変数が予期せず再配置される可能性があるため、移動可能な管理変数へのポインターはほとんど役に立ちません。C#コンパイラでは、固定ステートメントで管理変数へのポインタのみを割り当てることができます。

ガベージコレクション:Microsoft.NETFrameworkでの自動メモリ管理

固定ステートメント(C#リファレンス)

于 2012-05-19T22:24:37.603 に答える