Почему максимальный размер byte[] 2 GB-57 B?

на моей 64-битной машине этот код C# работает:

new byte[2L * 1024 * 1024 * 1024 - 57]

но этот бросает OutOfMemoryException:

new byte[2L * 1024 * 1024 * 1024 - 56]

Почему?

Я понимаю, что максимальный размер управляемого объекта составляет 2 ГБ и что объект массива, который я создаю, содержит больше байтов, которые я хочу. А именно, есть 4 байта (или 8?) для номера syncblock, 8 байт для ссылки MethodTable и 4 байта для размера массива. Это 24 байта, включая заполнение, так почему я не могу выделить массив с 2G - 24 байта? Является ли максимальный размер действительно ровно 2 ГБ? Если это так, то для чего используется остальная часть 2 GB?

Примечание: мне на самом деле не нужно выделять массив с 2 миллионами байтов. И даже если бы я это сделал, 56 байт незначительны накладные расходы. И я мог бы легко работать вокруг предела, используя custom BigArray<T>.

2 ответов


вам потребуется 56 байта. Это на самом деле 2,147,483,649-1 минус 56 для максимального размера. Вот почему минус 57 работ и минус 56 не.

As Джон Скит говорит здесь:

однако, с практической точки зрения, я не считаю, что любая реализация поддерживает такие огромные массивы. Среда CLR имеет ограничение на каждый объект немного меньше 2GB, таким образом, даже массив байтов не может на самом деле есть 2147483648 элементы. Немного эксперименты показывают, что на моей коробке, самый большой массив, который вы можете создать новый байт[2147483591]. (Это на 64 бит .NET CLR; версия Mono У меня на нем установлены дроссели.)

см. также статьи сайта informit на ту же тему. Он предоставляет следующий код для демонстрации максимальных размеров и накладных расходов:

class Program
{
  static void Main(string[] args)
  {
    AllocateMaxSize<byte>();
    AllocateMaxSize<short>();
    AllocateMaxSize<int>();
    AllocateMaxSize<long>();
    AllocateMaxSize<object>();
  }

  const long twogigLimit = ((long)2 * 1024 * 1024 * 1024) - 1;
  static void AllocateMaxSize<T>()
  {
    int twogig = (int)twogigLimit;
    int num;
    Type tt = typeof(T);
    if (tt.IsValueType)
    {
      num = twogig / Marshal.SizeOf(typeof(T));
    }
    else
    {
      num = twogig / IntPtr.Size;
    }

    T[] buff;
    bool success = false;
    do
    {
      try
      {
        buff = new T[num];
        success = true;
      }
      catch (OutOfMemoryException)
      {
        --num;
      }
    } while (!success);
    Console.WriteLine("Maximum size of {0}[] is {1:N0} items.", typeof(T).ToString(), num);
  }
}

наконец, в статье сказано:

Если вы делаете математику, вы увидите, что накладные расходы для выделения массива составляет 56 байт. Осталось несколько байтов в конце из-за размеров объекта. Например, 268,435,448 64-бит числа занимают 2,147,483,584 байта. Добавление 56 байтовый массив накладные дает вам 2,147,483,640, оставляя вам 7 байт не хватает 2 гигабайт.

Edit:

но подождите, есть больше!

памяти и строки. В этой статье он приводит таблицу размеров:
Type            x86 size            x64 size
object          12                  24
object[]        16 + length * 4     32 + length * 8
int[]           12 + length * 4     28 + length * 4
byte[]          12 + length         24 + length
string          14 + length * 2     26 + length * 2

г-н тарелочкам говорит:

Вы можете быть прощены за то, что смотрите цифры выше и думая, что "накладные расходы" объекта - 12 байты в x86 и 24 в x64... но это не совсем так.

и так:

  • есть" базовые " накладные расходы 8 байт на объект в x86 и 16 за объект в 64... учитывая, что мы можем хранить Int32 "реальных" данных в x86 и все еще имеют размер объекта 12, и также мы можем хранить два Int32s реальных данных в x64 и все-таки есть объект x64.

  • существует "минимальный" размер 12 байт и 24 байта соответственно. В другими словами, у вас не может быть типа это просто накладные расходы. Обратите внимание, как "пустой" класс занимает то же самое размер как создание экземпляров Объект... есть эффективно некоторые свободная комната, потому что CLR не как работать на объекте без данные. (Обратите внимание, что структура не поля тоже занимают место, даже для локальная переменная.)

  • объекты x86 дополняются до 4 байтовых границ; на x64 это 8 байт (как и прежде)--9-->

и, наконец, Джон Скит ответил на вопрос, который я задал ему в другом вопросе, где он заявляет (в ответ на статьи сайта informit я показал ему):

это похоже на статью, которую вы ссылка на вывод накладных расходов как раз от предела, который глупо ИМО.

Итак, чтобы ответить на ваш вопрос, фактические накладные расходы 24 байт С 32 байт свободной комнаты, насколько я понял.


одно можно сказать наверняка, что вы не можете иметь нечетное количество байтов, это обычно кратно размеру собственного слова, который составляет 8bytes на 64-битном процессе. Таким образом, вы можете добавить еще 7 байт в массив.