Страницы

Поиск по вопросам

воскресенье, 9 июня 2019 г.

Сколько памяти занимают объекты?

Провел несколько тестов, и был весьма удивлен... Режим компиляции x64. Первый тест показал 4 байта, здесь все нормально. Второй 32. Ого подумал я. И трейтий 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; }
Вопрос даже не в том почему во втором тесте один экземляр занимает 32 байта. А в том почему в третьем тесте экземпляр не занимает 40 байт.


Ответ

Во первых, в данном коде неправильно измеряется размер для массива ссылочных типов. Код:
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;
Измеряет память под массив ссылок + память по объекты. Надо так:
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;
Во вторых, арифметика Размер C = Размер object + 2 * Размер int не работает: все несколько сложнее.
В CLR существует минимальный размер объекта, см. 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))
Для 64-разрядной версии минимальный размер 2 * 8 + 8 = 24. Размер типа, меньшего 24 байта, дополняется до 24.
(Определение ObjHeader здесь: https://github.com/dotnet/coreclr/blob/master/src/gc/env/gcenv.object.h) Размер служебного блока, добавляемого к любому ссылочному типу, равен 16 байт (для x86 - 8 байт, см. например здесь, для x64 в два раза больше). Кроме того, предположительно, работает дополнение размера до числа, кратного 8.
Таким образом:
Размер объекта с 1 int полем = 24 байта Размер объекта с 2 int полями = 24 байта Размер объекта с 3 int полями = 32 байта

Комментариев нет:

Отправить комментарий