I did some tests and was very surprised ... x64 compile mode. The first test showed 4 bytes, everything is fine here. Second 32. Wow, I thought. And treyty 32 WTF ???

static int count = 10000000; static void Array_C() { var size = GC.GetTotalMemory(true); var arr = new int[count]; for (var i = 0; i < count; ++i) { arr[i] = i; } var mem = GC.GetTotalMemory(true) - size; Console.WriteLine("Выделено памяти: " + mem + ", размер одного объекта: " + Math.Truncate((double)mem / count) + ", код последней " + arr.Last()); } static void Array_E() { var size = GC.GetTotalMemory(true); var arr = new object[count]; for (var i = 0; i < count; ++i) { arr[i] = new object(); } var mem = GC.GetTotalMemory(true) - size; Console.WriteLine("Выделено памяти: " + mem + ", размер одного объекта: " + Math.Truncate((double)mem / count) + ", код последней " + arr.Last().GetHashCode()); } static void Array_F() { var size = GC.GetTotalMemory(true); var arr = new C[count]; for (var i = 0; i < count; ++i) { arr[i] = new C { Val0 = i, Val1 = i }; } var mem = GC.GetTotalMemory(false) - size; Console.WriteLine("Выделено памяти: " + mem + ", размер одного объекта: " + Math.Truncate((double)mem / count) + ", код последней " + arr.Last().Val0 + arr.Last().Val1); } [StructLayout(LayoutKind.Sequential, Pack = 1)] class C { public int Val0; public int Val1; } 

The question is not even why in the second test one instance takes 32 bytes. And that is why in the third test the instance does not occupy 40 bytes.

  • Well, you just do not measure it correctly (I don’t know how to measure it correctly, and in general, is it possible to measure it correctly) - Andrey NOP
  • And why exactly 40? - MSDN.WhiteKnight
  • @ AndreiNOP Why not right? The first test shows that everything is correct. int takes 4 bytes. - Max Burtsev
  • @ MSDN.WhiteKnight because 32 + 4 + 4. If an object is 32, then an object with two variables of 4 should take 40 - Max Burtsev
  • one
    @ MSDN.WhiteKnight 32 - 8 (per link) = 24 - PetSerAl

1 answer 1

Firstly, in this code, the size of an array of reference types is incorrectly measured. Code:

 var size = GC.GetTotalMemory(true); var arr = new object[count]; for (var i = 0; i < count; ++i) { arr[i] = new object(); } var mem = GC.GetTotalMemory(true) - size; 

Measures memory for an array of links + memory for objects. It is necessary so:

 var arr = new object[count]; var size = GC.GetTotalMemory(true); for (var i = 0; i < count; ++i) { arr[i] = new object(); } var mem = GC.GetTotalMemory(true) - size; 

Secondly, arithmetic Размер C = Размер object + 2 * Размер int does not work: everything is somewhat more complicated.

  1. The CLR has a minimum object size, see 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 the 64-bit version, the minimum size is 2 * 8 + 8 = 24. The size of a type smaller than 24 bytes is padded to 24.

    (ObjHeader definition here: https://github.com/dotnet/coreclr/blob/master/src/gc/env/gcenv.object.h )

  2. The size of the service block added to any reference type is 16 bytes (for x86 - 8 bytes, see, for example, here , for x64 it is twice as large).

  3. In addition, presumably, the addition of the size up to a multiple of 8 works.

In this way:

Object size with 1 int field = 24 bytes
Object size with 2 int fields = 24 bytes
Object size with 3 int fields = 32 bytes

  • I just need to count not only the size of the object but also a link to it. Because To build trees, it’s worth choosing between objects and references, or arrays and pointers. - Max Burtsev
  • It turns out that using pointers and manual memory management I can save more than 2 times. - Max Burtsev
  • @MaxBurtsev start better with arrays of structures, but not with pointers - Pavel Mayorov
  • @MaxBurtsev Yes, using pointers and an unsafe code, you can get a performance gain when processing large arrays. See for example here: social.msdn.microsoft.com/Forums/ru-RU/… - MSDN.WhiteKnight