Подтвердить что ты не робот

Сколько байтов распределено для 3-х точечных структур на 64-битном процессоре?

Возникает вопрос:

Дано:

struct Point {int x; int y;}
var p = new Point[3]

сколько байтов памяти будет выделено в стеке и в куче, если мы будем использовать 64-разрядный процессор?

Правильный ответ для .Net 44. Может ли кто-нибудь объяснить, как появилось это число?

Насколько я понимаю, p будет занимать 8 байт в стеке для x64.

И у нас есть два значения Int32 для каждой структуры, таким образом p.Length * sizeof(Point) 3 * 8 = 24 байта в куче для массива.

Это будет 32 байта. Что остальное 12 байт в этом случае?

4b9b3361

Ответ 1

Ваш ответ 44 байт, вероятно, является путаницей, связанной с массивом из 32-битной архитектуры.

В .Net (32 бит):

  • Каждый object содержит 4 байта для синхронизации (lock (obj)).
  • Каждый object содержит 4 байта своего токена типа.
  • Каждый array содержит 4 байта своей длины.

Указатель 8 байт, как вы сказали.

Это с 24 байтами самого массива дает вам 44 байта.


Однако, это макет заголовка для 32 бит.

Как вы можете видеть, макет памяти следующего кода:

var p = new Point[3];
p[0] = new Point { x = 1, y = 2 };
p[1] = new Point { x = 3, y = 4 };
p[2] = new Point { x = 5, y = 6 };

var p2 = new Point[3];
p2[0] = new Point { x = 8, y = 8 };
p2[1] = new Point { x = 8, y = 8 };
p2[2] = new Point { x = 8, y = 8 };

Будет:

Макет памяти

Вы также можете увидеть числовые значения в макете памяти.


В 64-битном поле каждое поле заголовка имеет 8 байт, поэтому длина заголовка 24 байта, поэтому длина всего массива 48 байт и с переменной, указывающей на массив: 56 байт.

64-битная архитектура:

64-битная макет памяти


Примечания:

  • Если ваш массив не был округлен до 8-битного множественного выравнивания, он будет иметь место, но это не требует выравнивания. Пример (два массива размера int 1):

    Выравнивание

  • Даже если поле длины заголовка 8 байт в 64 бит, оно больше максимального размера массива .Net, поэтому можно использовать только 4.

Имейте в виду, что это деталь реализации, и она может меняться между реализациями/версиями CLR.

Ответ 2

Большая часть этого является исключительно детальностью реализации и может измениться со следующей версией CLR.

Запустите следующую программу как x86 или x64, и вы можете эмпирически определить размер вашей структуры:

struct Point { int x; int y; }

class Program
{
    const int Size = 100000;

    private static void Main(string[] args)
    {
        object[] array = new object[Size];
        long initialMemory = GC.GetTotalMemory(true);
        for (int i = 0; i < Size; i++)
        {
            array[i] = new Point[3];
        }
        long finalMemory = GC.GetTotalMemory(true);
        GC.KeepAlive(array);

        long total = finalMemory - initialMemory;
        Console.WriteLine("Size of each element: {0:0.000} bytes",
                          ((double)total) / Size);

    }
}

Код довольно прост, но бесстыдно украден у Jon Skeet.

Если вы запустите это, вы получите следующие результаты:

x86: 36 byte
x64: 48 byte

В текущей реализации размер каждого объекта выравнивается с размером указателя, это означает, что каждый объект в x86 равен 4 байтам, а под x64 8 байтами (это абсолютно возможно изменить - HotSpot в Java, например, выравнивает все до 8 байтов даже под x86).

Массивы в С# несколько отличаются своей длиной: в то время как у них есть поле длиной 4 байта, под x64 они также включают в себя 4 байта дополнительного дополнения (vm/object.h: 766 содержит интересную часть). Это, скорее всего, сделано, чтобы гарантировать, что начало фактических полей всегда равно 8 байтам, выровненным под x64, что необходимо для получения хорошей производительности при доступе к longs/doubles/pointers (альтернативой было бы только добавление дополнения для этих типов и специализация вычисление длины - вряд ли будет стоить дополнительной сложности).

В x86 заголовок объекта имеет 8 байт и служебный 4 байта массива, что дает нам 36 байт.

В x64 заголовок объекта составляет 16 байт, а служебный 8-байтовый массив. Это дает нам 24 + 24 = 48 байт.

Для тех, кто хочет фактическое доказательство вместо эмпирических тестов о размере и выравнивании заголовка, вы можете просто перейти к фактическому источнику: Здесь - объект определение coreclr. Отметьте комментарий, начинающийся со строки 178, который гласит:

// The only fields mandated by all objects are
// 
//     * a pointer to the code:MethodTable at offset 0
//     * a poiner to a code:ObjHeader at a negative offset. This is often zero.  It holds information that
//         any addition information that we might need to attach to arbitrary objects. 

Вы также можете посмотреть фактический код, чтобы увидеть, что эти указатели являются фактическими указателями, а не DWORD или чем-либо еще.

Код для выравнивания размеров объекта также находится в одном файле:

#define PTRALIGNCONST (DATA_ALIGNMENT-1)

#ifndef PtrAlign
#define PtrAlign(size) \
    ((size + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign

DATA_ALIGNMENT определяется как 4 для x86 (vm/i386/cgencpu.h) и ARM (vm/arm/cgencpu.h) и 8 для x64 (vm/amd64/cgencpu.h). Сам код является ничем иным, как стандартным оптимизированным "от кругового к следующему краю DATA_ALIGNMENT", при условии, что выравнивание данных является степенью 2-х методов.

Ответ 3

Говоря о архитектуре x86, ответ 44 байт неверен, поскольку размер ссылки на объект в x86 составляет 4 байта, а не 8 байтов, поэтому длина объекта 36 байт + 4 байта от ссылки на объект дает 40 байт. Исправьте меня, если я ошибаюсь.