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

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

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

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

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

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

Почему?

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

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

4b9b3361

Ответ 1

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

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

Однако, на практике, я не полагайте, что любая поддержка реализации такие огромные массивы. CLR имеет для одного объекта, немного меньше 2 ГБ, поэтому даже байт-массив не может имеют 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 на объект в x64... учитывая, что мы можем хранить Int32 "реальных" данных в x86 и все еще имеют размер объекта 12, и мы также можем хранить два Int32s реальных данных в x64 и все еще имеют объект x64.

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

  • Объекты x86 дополняются 4 байтовыми границами; на x64 это 8 байт (как и раньше)

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

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

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

Ответ 2

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