📜 ⬆️ ⬇️

Dimensions of CLR objects. Precise definition

I think many developers on managed code have always wondered: how many bytes does an object instance occupy? What is the limit on the size of a single object in the CLR? Are there any differences in memory allocation between 32-bit and 64-bit systems? If these questions are not an empty sound for you, then I ask for cat.

Foreword

First, remember that in .NET there are 2 types of objects: value types and reference types , which are created, respectively, in the stack and heap (managed by the garbage collector).
Value types are designed to store simple data, be it a number, a symbol. During assignment of a variable value, each object field is copied. Also, the lifetime of such objects depends on the scope. The sizes of the value types are defined in the Common Type System and are:
CTS-TypeNumber of bytes
System.Byteone
System.SByteone
System.Int162
System.Int32four
System.Int64eight
System.UInt162
System.UInt32four
System.UInt64eight
System.Singlefour
System.Doubleeight
System.Char2
System.Decimalsixteen

Reference types, on the other hand, are a reference to the memory area occupied by the object instance on the heap.

The following is the internal structure of CLR objects:


')
For variable reference types, a fixed-size value (4 bytes, DWORD type) containing the address of the object instance created in the regular heap is placed on the stack (there is also a Large Object Heap, HighFrequencyHeap, etc., but we will not focus on them ). For example, in C ++, this value is called a pointer to an object, and in the .NET world, a reference to an object.

The initial value of SyncBlock is zero. However, the object hash code can be stored in SyncBlock (when calling the GetHashCode method), or the syncblk record number , which places the environment in the object header during synchronization (using lock , or directly by Monitor.Enter ).

Each type has its own MethodTable, and all instances of objects of the same type refer to the same MethodTable. This table stores information about the type itself (interface, abstract class, etc.).

Reference type pointer - a reference to an object stored in a variable placed on the stack with an offset of 4.
The rest is class fields.

SOS

Let's move from theory to practice. Standard CLR tools cannot set object size. Yes, there is a sizeof operator in C #, but it is intended to establish the size of unmanaged objects, as well as the size types. In matters of reference types, it is useless.

For these purposes, there is the Visual Studio debugger extension - SOS (Son of Strike) .

Before use, you must enable unmanaged code debugging:



To activate SOS, during debugging you need to open VS> Debug> Windows> Immediate Window and enter the following:
.load sos.dll

After which we will see its successful download:



SOS has a large number of commands. In our case, only the following will be needed:

The remaining commands can be found by typing ! Help .

To demonstrate, create a simple console application and write the class MyExampleClass :

 class MyExampleClass { byte ByteValue = 255; // 1  sbyte SByteValue = 127; // 1  char CharValue = 'a'; // 2  short ShortValue = 128; // 2  ushort UShortValue = 65000; // 2  int Int32Value = 255; // 4  uint UInt32Value = 255; // 4  long LongValue = 512; // 8  ulong ULongValue = 512; // 8  float FloatValue = 128F; // 4  double DoubleValue = 512D; // 8  decimal DecimalValue = 10M; // 16  string StringValue = "String"; // 4  } 

Take a calculator and calculate the estimated size for a class instance - so far 64 bytes.

However, remember at the beginning of the article about the structure of objects? So the final size will be equal to:
CLR-object = SyncBlock (4) + TypeHandle (4) + Fields (64) = 72

Let's check the theory.
Add the following code:

 class Program { static void Main(string[] args) { var myObject = new MyExampleClass(); Console.ReadKey(); //  breakpoint } } 

And we will start debugging (F5).
We introduce the following commands in the Immediate Window:

.load sos.dll
!DSO



In the screenshot above, the address of the myObject object is highlighted, which we pass as a parameter to the! DO command:



Well, the size of myObject is 72 bytes. Is not it?
The answer is no. The point is that we forgot to add the size of the string to the variable StringValue. Its 4 bytes is only a link. But the true size, we now check.

Let's enter the command! ObjSize:



Thus, the true size of myObject is 100 bytes.

An additional 28 bytes is occupied by the StringValue variable.

However, check it out. For this we use the address of the variable StringValue 01b8c008:



What is the size of System.String?

First, in CTS, characters (type System.Char ) are represented in Unicode and occupy 2 bytes.

Secondly, a string is nothing more than an array of characters. So in StringValue we wrote down the value “String”, which is 12 bytes.

Thirdly, System.String is a reference type, which means that it is located in the GC Heap, and will consist of SyncBlock, TypeHandle, Reference point + the rest of the class fields. Reference point here will not be taken into account, because already counted in the class MyExampleClass (link 4 bytes).

Fourth, the System.String structure is as follows:



Additional fields of the class are m_stringLength variables of the Int32 type (4 bytes), m_firstChar of the Char type (2 bytes), the Empty variable will not be considered, since is an empty static string.

Also pay attention to the size - 26 bytes instead of 28, counted earlier. Put it all together:
StringValue = SyncBlock (4) + TypeHandle (4) + m_stringLength (4) + m_firstChar (2) + “String” (12) = 26

The additional 2 bytes are generated due to the alignment produced by the CLR memory manager.

x86 vs. x64

The main difference is in the size of the DWORD - memory pointer. In 32-bit systems it is 4 bytes, in 64-bit systems it is already 8 bytes.
So, if the empty class will be equal in x86 only 12 bytes, then in x64 it is already 24 bytes.

CLR object size limit

It is considered that the size of System.String is limited only by the available system memory.

However, any instance of any type can not occupy more than 2 Gb of memory. And this restriction applies to both x86 and x64 systems.

Thus, the List, although it has the LongCount () method, this does not mean that it is possible to arrange 2 ^ 64 objects. The solution may be to use the BigArray class intended for this purpose.

Afterword

In this article, I wanted to touch on the issue of finding the sizes of CLR objects. Of course, there are pitfalls, especially with the! ObjSize command, when double counting can occur due to the use of intern lines.

For the most part, the question of the size of objects, their alignment in memory comes only when there is a strong need - the possibility of optimizing the use of resources.

I hope the article was interesting and useful. Thanks for attention!

useful links

Source: https://habr.com/ru/post/136609/


All Articles