共用方式為


分析 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])

所有這些回呼都是在 Common Language Runtime (CLR) 暫止時執行的。 因此,等到執行階段繼續並發生另一個記憶體回收時,才會變更 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 1.0 和 1.1 版中,分析工具無法偵測未被非壓縮記憶體回收 (完全沒有移動任何物件的記憶體回收) 所回收的物件。 .NET Framework 2.0 版透過下列新方法,針對這個案例提供更好的支援:

請注意,單一記憶體回收對於某個層代可以是壓縮的,而對於另一個層代則是非壓縮的。 亦即,對於指定的記憶體回收,任何指定的層代將會接收 SurvivingReferencesMovedReferences 回呼,但不會同時接收兩者。

備註

在記憶體回收之後,應用程式會停止,直到執行階段完成傳遞堆積資訊給程式碼分析工具。 您可以使用 ICorProfilerInfo::GetClassFromObject 方法,取得物件類別的 ClassID。 使用 ICorProfilerInfo::GetClassIDInfoICorProfilerInfo2::GetClassIDInfo2 方法,取得類別的相關中繼資料資訊。

在 .NET Framework 1.0 和 1.1 版中,當完成記憶體回收作業時,每個未被回收的物件預期是根參考、具有當做根參考的父代,或存在於未被回收的層代中。 有時候,物件可能不屬於以上任何分類。 這些物件可能是由執行階段內部配置或是委派的弱式參考。 在 .NET Framework 1.0 和 1.1 中,分析 API 不會讓使用者識別這些物件。

在 .NET Framework 2.0 版中,加入三個方法,協助分析工具精確釐清哪些層代在何時回收,並識別哪些物件是根。 這些方法會協助分析工具判斷在回收後為何物件繼續存在:

ICorProfilerCallback2::RootReferences2 方法可讓分析工具識別透過特殊控制代碼儲存的物件。 ICorProfilerInfo2::GetGenerationBounds 方法提供的層代界限資訊結合 ICorProfilerCallback2::GarbageCollectionStarted 方法提供的已回收層代資訊,可讓分析工具識別層代中未被回收的物件。

參考資料

ICorProfilerCallback::MovedReferences 方法

ICorProfilerCallback2::SurvivingReferences 方法

ICorProfilerInfo2::GetGenerationBounds 方法

請參閱

概念

分析概觀