GC Heap and Alignment Padding

The docs for GetObjectSize have recently been updated with this info, but I wanted to mention it here, too, to ensure you were aware of this information.

Some profilers manually advance through objects on the heap, inspecting their field values, by starting at an ObjectID and moving forward by its size to the next ObjectID, repeating this process, for all the reported generation ranges (via GetGenerationBounds or MovedReferences/SurvivingReferences).  If your profiler doesn’t do this, then this blog entry will be of no interest to you, and you can skip it.  But if your profiler does do this, you need to be aware of the alignment rules that the CLR employs as it allocates and moves objects around on the GC heap.

  • On x86: All objects are 4-byte aligned, except for objects on the large-object-heap, which are always 8-byte aligned.
  • On x64: All objects are always 8-byte aligned, in all generations.

And the important point to note is that GetObjectSize does NOT include alignment padding in the size that it reports.  Thus, as your profiler manually skips from object to object by using GetObjectSize() to determine how far to skip, your profiler must manually add in any alignment padding necessary to achieve the alignment rules listed above.

Comments

  • Anonymous
    January 04, 2012
    Here is what I've used in the past to calculate object size (including alignment padding)..... ULONG ObjectInfo::GetAlignedSizeInternal(COR_PRF_GC_GENERATION gen, ULONG cbObjRawSize) { #ifdef X86    return (COR_PRF_GC_LARGE_OBJECT_HEAP == gen)           ? (cbObjRawSize + (2 * sizeof(ULONG_PTR)) - 1) & ~((2 * sizeof(ULONG_PTR)) - 1)                : (cbObjRawSize + sizeof(ULONG_PTR) - 1) & ~(sizeof(ULONG_PTR) - 1); #else    return (cbObjRawSize + sizeof(ULONG_PTR) - 1) & ~(sizeof(ULONG_PTR) - 1); #endif }