次の方法で共有


プロファイル API でのオブジェクト追跡

ガベージ コレクションは、無効なオブジェクトが使用するメモリをクリアし、解放された領域を圧縮できます。 そのため、有効なオブジェクトはヒープ内で移動されます。 ここでは、オブジェクトの移動が ObjectID の値に与える影響、および圧縮ガベージ コレクションおよび非圧縮ガベージ コレクションの間にプロファイル API がこれらの値を追跡する方法について説明します。

オブジェクトの移動

オブジェクトが移動する際には、前の通知で割り当てられた ObjectID 値が変化します。 オブジェクト自体の内部状態は、他のオブジェクトへの参照を除いて変化しません。 メモリ内でのオブジェクトの場所 (したがって ObjectID) だけが変化します。 ICorProfilerCallback::MovedReferences 通知により、プロファイラーは ObjectID で情報を追跡している内部テーブルを更新できます。 MovedReferences は移動されなかったオブジェクトに対しても実行されるため、このメソッド名は若干誤解を招く可能性があります。

ヒープ内のオブジェクト数は、数千または数百万になる可能性があります。 このように膨大な数のオブジェクトの移動を、前後の ID を提供することで追跡するのは現実的ではありません。 したがって、ガベージ コレクターは、連続したライブ オブジェクトをブロック単位で移動し、ヒープ内の新しい場所でも連続したままにする傾向があります。 MovedReferences 通知は、このような連続したオブジェクト ブロックの移動前後の ObjectID を報告します。

既存の ObjectID 値 (oldObjectID) が次の範囲内にあるとします。

oldObjectIDRangeStart[i] <= oldObjectID < oldObjectIDRangeStart[i] + cObjectIDRangeLength[i]

この場合、範囲の先頭からオブジェクトの先頭までのオフセットは次のようになります。

oldObjectID - oldObjectRangeStart[i]

i の各値の有効範囲は次のとおりです。

0 <= i < cMovedObjectIDRanges

次のようにして、新しい ObjectID を計算できます。

newObjectID = newObjectIDRangeStart[i] + (oldObjectID – oldObjectIDRangeStart[i])

これらのコールバックはすべて、共通言語ランタイム (CLR: Common Language Runtime) の中断中に行われます。 したがって、ランタイムが再開して別のガベージ コレクションが発生するまで、ObjectID の値は変更できません。

次の図は、ガベージ コレクション前の 10 のオブジェクトを示しています。 これらの開始アドレス (ObjectID と等しい) は、08、09、10、12、13、15、16、17、18、および 19 です。 ObjectID が 09、13、および 19 のオブジェクトは死んだ状態であり、これらの領域はガベージ コレクション時にクリアされます。

ガベージ コレクションの間のオブジェクトの移動

ガベージ コレクション中のオブジェクト移動

図の下の部分は、ガベージ コレクション後のオブジェクトを示しています。 無効なオブジェクトによって占められていた領域は、有効なオブジェクトを保持するためにクリアされています。 ヒープ内の有効なオブジェクトは、図で示されている新しい場所に移動されています。 その結果、ObjectID は変化します。 次の表は、ガベージ コレクションの前と後の ObjectID を示しています。

オブジェクト

oldObjectIDRangeStart[]

newObjectIDRangeStart[]

0

08

07

1

09

2

10

08

3

12

10

4

13

5

15

11

6

16

12

7

17

13

8

18

14

9

19

次の表は、連続するブロックの開始位置とサイズを指定することで、情報を簡潔にしています。 この表は、MovedReferences メソッドが情報を報告する方法を正確に示しています。

ブロック

oldObjectIDRangeStart[]

newObjectIDRangeStart[]

cObjectIDRangeLength[]

0

08

07

1

1

10

08

2

2

15

11

4

すべての削除されたオブジェクトの検出

MovedReferences メソッドは、圧縮ガベージ コレクションで残ったすべてのオブジェクトを、移動されたかどうかに関係なく報告します。 MovedReferences で報告されないオブジェクトは残っていません。 ただし、すべてのガベージ コレクションが圧縮であるわけではありません。 .NET Framework Version 1.0 および 1.1 では、プロファイラーは、非圧縮ガベージ コレクション (どのオブジェクトもまったく移動しないガベージ コレクション) で残ったオブジェクトを検出できませんでした。 .NET Framework Version 2.0 では、以下の新しいメソッドにより、このようなシナリオのサポートが向上しています。

  • プロファイラーは、ICorProfilerInfo2::GetGenerationBounds メソッドを呼び出して、ガベージ コレクション ヒープ セグメントの境界を取得できます。 結果の COR_PRF_GC_GENERATION_RANGE 構造体の rangeLength フィールドを使用して、圧縮されたジェネレーションにおける有効なオブジェクトの範囲を判別できます。

  • ICorProfilerCallback2::GarbageCollectionStarted コールバックは、現在のガベージ コレクションで収集されているジェネレーションを示します。 収集されていないジェネレーションのすべてのオブジェクトは、ガベージ コレクション後も残ります。

  • ICorProfilerCallback2::SurvivingReferences コールバックは、非圧縮ガベージ コレクションで残ったオブジェクトを示します。

単一のガベージ コレクションで、あるジェネレーションは圧縮され、別のジェネレーションは圧縮されない場合があります。 つまり、特定のジェネレーションは、特定のガベージ コレクションに対して SurvivingReferences コールバックまたは MovedReferences コールバックのどちらかを受け取りますが、両方を受け取ることはありません。

解説

ガベージ コレクションの後、ランタイムがヒープに関する情報をコード プロファイラーに渡し終わるまで、アプリケーションは停止します。 ICorProfilerInfo::GetClassFromObject メソッドを使用すると、オブジェクトのクラスの ClassID を取得できます。 ICorProfilerInfo::GetClassIDInfo メソッドまたは ICorProfilerInfo2::GetClassIDInfo2 メソッドを使用すると、クラスに関するメタデータ情報を取得できます。

.NET Framework Version 1.0 および 1.1 では、ガベージ コレクションの操作が完了した時点で、残っているすべてのオブジェクトは、ルート参照である、ルート参照である親を持っている、または収集されなかったジェネレーションに存在するものと考えられます。 場合によっては、これらのどのカテゴリにも属さないオブジェクトが存在する可能性があります。 このようなオブジェクトは、ランタイムによって内部的に割り当てられているか、またはデリゲートに対する弱い参照です。 .NET Framework 1.0 および 1.1 では、プロファイル API を使用してこのようなオブジェクトを識別することはできません。

.NET Framework Version 2.0 で追加された 3 つのメソッドを使用すると、プロファイラーは、どのジェネレーションがいつ収集されるのかを明確にし、どのオブジェクトがルートであるかを識別できます。 これらのメソッドを使用することで、プロファイラーはコレクション後にオブジェクトが残っている理由を判別できます。

ICorProfilerCallback2::RootReferences2 メソッドは、特殊なハンドルで保持されているオブジェクトを識別します。 ICorProfilerInfo2::GetGenerationBounds メソッドで提供されるジェネレーションの境界情報と、ICorProfilerCallback2::GarbageCollectionStarted メソッドで提供される、収集されたジェネレーションの情報を組み合わせると、収集されなかったジェネレーションに存在するオブジェクトをプロファイラーで識別できます。

参照

ICorProfilerCallback::MovedReferences メソッド

ICorProfilerCallback2::SurvivingReferences メソッド

ICorProfilerInfo2::GetGenerationBounds メソッド

参照

概念

プロファイリングの概要