How Big Is an Object Reference in .Net

How big is an object reference in .NET?

The reference itself is basically a pointer. 32 bits on a 32 bit OS, 64 bits on a 64 bit OS.

The size of the object that's referenced is more complicated.

How much memory does a C# reference consume?

A reference is implemented as a pointer, so in an application that runs in x86 mode (32 bit), a reference is four bytes, and in x64 mode (64 bit), a reference is eight bytes.

As the reference is just a pointer to an object, the reference is the same size regardless of what it is pointing to, or even if it doesn't point to anything at all (null).

Size of A Class (object) in .NET

The size of a class instance is determined by:

  • The amount of data actually stored in the instance
  • The padding needed between the values
  • Some extra internal data used by the memory management

So, typically a class containing a string property needs (on a 32 bit system):

  • 8 bytes for internal data
  • 4 bytes for the string reference
  • 4 bytes of unused space (to get to the minimum 16 bytes that the memory manager can handle)

And typically a class containing an integer property needs:

  • 8 bytes for internal data
  • 4 bytes for the integer value
  • 4 bytes of unused space (to get to the minimum 16 bytes that the memory manager can handle)

As you see, the string and integer properties take up the same space in the class, so in your first example they will use the same amount of memory.

The value of the string property is of course a different matter, as it might point to a string object on the heap, but that is a separate object and not part of the class pointing to it.

For more complicated classes, padding comes into play. A class containing a boolean and a string property would for example use:

  • 8 bytes for internal data
  • 1 byte for the boolean value
  • 3 bytes of padding to get on an even 4-byte boundary
  • 4 bytes for the string reference

Note that these are examples of memory layouts for classes. The exact layout varies depending on the version of the framework, the implementation of the CLR, and whether it's a 32-bit or 64-bit application. As a program can be run on either a 32-bit or 64-bit system, the memory layout is not even known to the compiler, it's decided when the code is JIT:ed before execution.

Why is the minimum size of a reference type 12 bytes for a 32 bit .NET process

Minimum of 12 bytes is a requirement of the garbage collection implementation.

From here: http://msdn.microsoft.com/en-us/magazine/cc163791.aspx#S9

The Base Instance Size is the size of the object as computed by the class loader, based on the field declarations in the code. As discussed previously, the current GC implementation needs an object instance of at least 12 bytes. If a class does not have any instance fields defined, it will carry an overhead of 4 bytes. The rest of the 8 bytes will be taken up by the Object Header (which may contain a syncblk number) and TypeHandle.

(TypeHandle being a handle to the method table).

how are C# object references represented in memory / at runtime (in the CLR)?

This answer is most easily understood if you understand C/C++ pointers. A pointer is a simply the memory address of some data.

  1. An object reference should be the size of a pointer, which is normally 4 bytes on a 32-bit CPU, and 8 bytes on a 64-bit CPU. It is the same regardless of where it is defined. Where it lives does depend on where it is defined. If it is a field of a class, it will reside on the heap in the object it is part of. If it is a static field, it is located in a special section of the heap that is not subject to garbage collection. If it is a local variable, it lives on the stack.

  2. An object reference is simply a pointer, which can be visualized as an int or long containing the address of the object in memory. It is the same regardless of where it is defined.

  3. This is implemented as a pointer to a pointer. The data is the same - just a memory address. However, there is no object at the given memory address. Instead, there is another memory address, which is the original reference to the object. This is what allows a reference parameter to be modified. Normally, a parameter disappears when its method completes. Since the reference to the object is not a parameter, then changes to this reference will remain. The reference to a reference will disappear, but not the reference. This is the purpose for passing reference parameters.

One thing you should know, value types are stored in place (there is no memory address, instead they are stored directly where the memory address would be - See #1). When they are passed to a method, a copy is made and that copy is used in the method. When they are passed by reference, a memory address is passed which locates the value type in memory, allowing it to be changed.

Edit: As dlev pointed out, these answers are not the hard and fast rule, since there is no rule that says this is how it must be. .NET is free to implement these questions however it wants. This is the most likely way to implement it though, as this is how the Intel CPU's work internally, so using any other method would likely be inefficient.

Hope I didn't confuse you too much, but feel free to ask if you need clarification.

Understanding CLR object size between 32 bit vs 64 bit

The CLR is free to lay out objects in memory as it sees fit. It's an implementation detail. You should not rely on any specific layout.

The difference you see is due to the missing TypeHandle field which is also a part of the CLR object header. Additionally, the fields may be aligned to byte boundaries.


From Advanced .Net Debugging - CLR Object’s Internal Structure:

An object’s CLR internal structure is:

[DWORD: SyncBlock][DWORD: MethodTable Pointer][DWORD: Reference type pointer]…[Value of Value Type field]…

Object Header: [DWORD: SyncBlock]

Object Pointer: [DWORD: MethodTable Pointer][DWORD: Reference type pointer]…[Value of Value Type field]…

Every Object is preceded by an ObjHeader (at a negative offset). The ObjHeader has an index to a SyncBlock.


So your object is likely laid out like this:

x86: (aligned to 8 bytes)


Syncblk TypeHandle X Y
------------,------------|------------,------------|
8 16

x64: (aligned to 8 bytes)


Syncblk TypeHandle X Y
-------------------------|-------------------------|------------,------------|
8 16 24

See also: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

C# Object Size Overhead

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.

.NET Object size

4 byte boundaries on x86. Possibly 8 byte boundaries on x64.

There's an 8 byte overhead on x86, for a type reference and a sync block. I wouldn't be surprised to find that's 12 or 16 bytes on x64.

For some reason, on x86 an instance of just System.Object appears to take 12 bytes, making 12 bytes the absolute minimum size possible - but a class with an int also takes 12 bytes. I've no idea why this is the case.

.NET object size limit

.NET limits any object to max 2 GB even on 64 bit platforms. You can create your own data type, that uses multiple objects to store more data, thus getting around the 2 GB limit of a single object. For instance a List<float[]> would allow you to store more than 2 GB, but you would have to write the necessary plumbing code to make it behave similar to a single, large array.

You may also want to check this question.



Related Topics



Leave a reply



Submit