I am working on optimization of memory consuming application. In relation to that I have question regarding C# reference type size overhead.
The C# object consumes as ma
There are two types of overhead for an object:
The internal data is two pointers, so in a 32-bit application that is 8 bytes, and in a 64-bit application that is 16 bytes.
Data members are padded so that they start on an even address boundary. If you for example have a byte
and an int
in the class, the byte
is probably padded with three unused bytes so that the int
starts on the next machine word boundary.
The layout of the classes is determined by the JIT compiler depending on the architecture of the system (and might vary between framework versions), so it's not known to the C# compiler.
There is an article online with the title "The Truth About .NET Objects And Sharing Them Between AppDomains" which shows some rotor source code and some results of experimenting with objects and sharing them between appdomains via a plain pointer.
http://geekswithblogs.net/akraus1/archive/2012/07/25/150301.aspx
You can do test this quite easily by adding millions of objects (N) to an array. Since the pointer size is known you can calculate the object size by dividing the value of
var intial = GC.GetTotalMemory(true)
const int N=10*1000*1000;
var arr = new object[N];
for(int i=0;i<N;i++)
{
arr[i] = new object();
}
var ObjSize = (initial-GC.GetTotalMemory(false)-N*IntPtr.Size)/N
to get an approximate value on your .NET platform.
The object size is actually a define to allow the GC to make assumptions about the minimum object size.
\sscli20\clr\src\vm\object.h
//
// The generational GC requires that every object be at least 12 bytes
// in size.
#define MIN_OBJECT_SIZE (2*sizeof(BYTE*) + sizeof(ObjHeader))
For e.g. 32 bit this means that the minmum object size is 12 bytes which does leave a 4 byte hole. This hole is empty for an empty object but if you add e.g. an int to your empty class then it is filled and the object size stays at 12 bytes.
Typically, there is an 8 or 12 byte overhead per object allocated by the GC. There are 4 bytes for the syncblk and 4 bytes for the type handle on 32bit runtimes, 8 bytes on 64bit runtimes. For details, see the "ObjectInstance" section of Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects on MSDN Magazine.
Note that the actual reference does change on 32bit or 64bit .NET runtimes as well.
Also, there may be padding for types to fit on address boundaries, though this depends a lot on the type in question. This can cause "empty space" between objects as well, but is up to the runtime (mostly, though you can affect it with StructLayoutAttribute) to determine when and how data is aligned.