When objects are moved, ObjectID values that were assigned by previous notifications change. The internal state of the object itself does not change, except for its references to other objects. Only the object's location in memory (and therefore its ObjectID) changes. The ICorProfilerCallback::MovedReferences notification lets a profiler update its internal tables that track information by ObjectID. The MovedReferences method name is somewhat misleading, because it is issued even for objects that were not moved.
The number of objects in the heap can number thousands or millions. It would be impractical to track the movement of such a large number of objects by providing a before and after ID for each object. Therefore, the garbage collector tends to move contiguous live objects in blocks, so they stay contiguous in their new locations in the heap. The MovedReferences notification reports the before and after ObjectIDs of these contiguous blocks of objects.
Assume that an existing ObjectID value (oldObjectID) lies within the following range:
oldObjectIDRangeStart[i] <= oldObjectID < oldObjectIDRangeStart[i] + cObjectIDRangeLength[i]
In this case, the offset from the start of the range to the start of the object is as follows:
oldObjectID - oldObjectRangeStart[i]
For any value of i that is in the following range:
0 <= i < cMovedObjectIDRanges
you can calculate the new ObjectID as follows:
newObjectID = newObjectIDRangeStart[i] + (oldObjectID – oldObjectIDRangeStart[i])
All these callbacks are made while the common language runtime (CLR) is suspended. Therefore, none of the ObjectID values can change until the runtime resumes and another garbage collection occurs.
The following illustration shows 10 objects before garbage collection. Their start addresses (equivalent to ObjectIDs) are 08, 09, 10, 12, 13, 15, 16, 17, 18, and 19. The objects with ObjectIDs 09, 13, and 19 are dead, and their space will be reclaimed during garbage collection.
Object movement during garbage collection
.png)
The lower part of the illustration shows the objects after garbage collection. The space that was occupied by the dead objects has been reclaimed to hold live objects. The live objects in the heap have been moved to the new locations that are shown. As a result, their ObjectIDs will change. The following table shows the ObjectIDs before and after garbage collection.
Object | 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 | |
The following table compacts the information by specifying the starting positions and sizes of contiguous blocks. This table shows exactly how the MovedReferences method reports the information.
Blocks | oldObjectIDRangeStart[] | newObjectIDRangeStart[] | cObjectIDRangeLength[] |
|---|
0 | 08 | 07 | 1 |
1 | 10 | 08 | 2 |
2 | 15 | 11 | 4 |