There is a fixed overhead associated with a .NET object as more fully outlined in this SO question: What is the memory overhead of a .NET Object being 12 or 24 bytes depending o
The size of a struct is determined by the sum of the sizes of its fields plus the padding between the fields that get them aligned properly, plus padding at the end of the struct that ensures that they are still aligned properly when the struct is stored in an array.
So, for one, a struct is not entirely unlikely to contain a field of a reference type. Like a string. In which case the struct is going to be larger since references are pointers under the hood, taking 8 bytes instead of 4.
The padding is the much sneakier detail. In 32-bit mode, variables cannot count on an alignment better than 4. An issue with double
and long
, 8 byte types that can easily get mis-aligned. Notably affecting perf of a 32-bit program, if a double is misaligned across an L1 cache boundary line then a read or write can be 3x as slow. Also the core reason that these types are not atomic in the C# memory model. Not an issue in 64-bit mode, the CLR then must and does provide an alignment guarantee of 8.
Nevertheless, the CLR does attempt to give such struct members proper alignment in 32-bit mode, even though the struct itself is not guaranteed to be aligned. Otherwise a side-effect of structs having an implicit [StructLayout(LayoutKind.Sequential, Pack=8)]
attribute. An oddity in the CLR source code, the C++ statement that does this has no comment. I suspect it was a quicky fix for less than stellar unmanaged interop perf, keeping structs blittable is pretty important to speed.
You'll however not always get this, the CLR gives up if the struct contains a member that is itself a struct that does not have sequential layout. Notably this happens for DateTime
and DateTimeOffset
, the programmers who wrote them applied the [StructLayout(LayoutKind.Auto)] attribute on them for very mysterious reasons. In the case of DateTimeOffset likely to be a copy/paste bug. Layout of your struct will now be unpredictable, it becomes LayoutKind.Auto as well and the CLR re-arranges fields to minimize the struct size. This can cause extra padding in x64 mode.
These are obscure implementation details that you should never fret about.