为什么byte []的最大大小为2 GB - 57 B?

svi*_*ick 28 .net arrays limits

在我的64位机器上,这个C#代码有效:

new byte[2L * 1024 * 1024 * 1024 - 57]
Run Code Online (Sandbox Code Playgroud)

但是这个抛出一个OutOfMemoryException:

new byte[2L * 1024 * 1024 * 1024 - 56]
Run Code Online (Sandbox Code Playgroud)

为什么?

我知道托管对象的最大大小是2 GB,我正在创建的数组对象包含的内容超过了我想要的字节数.也就是说,同步块编号有4个字节(或8?),MethodTable参考有8个字节,数组大小有4个字节.这是24个字节,包括填充,所以为什么我不能分配一个2G-24字节的数组?最大尺寸真的 2 GB吗?如果是这样的话,2 GB的剩余部分用于什么?

注意:我实际上不需要分配一个包含200万字节的数组.即使我这样做,56个字节的开销可以忽略不计.我可以使用自定义轻松解决限制BigArray<T>.

小智 20

您需要56个字节的开销.最大尺寸实际上是2,147,483,649-1 减去 56.这就是为什么你的减去 57工作而减去 56工作没有.

正如Jon Skeet在这里所说:

但是,实际上,我认为任何实现都不支持这样庞大的数组.CLR的每个对象限制有点短于2GB,因此即使是字节数组也不能实际具有2147483648个元素.一些实验表明,在我的盒子上,你可以创建的最大数组是新字节[2147483591].(这是在64位.NET CLR上; Mono的版本已经安装了choke.)

另请参阅有关同一主题的此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);
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,文章有这样的说法:

如果你做数学运算,你会发现分配数组的开销是56字节.由于对象大小,最后会留下一些字节.例如,268,435,448个64位数字占用2,147,483,584字节.添加56字节的数组开销为您提供2,147,483,640,留下7字节短2千兆字节.

编辑:

但等等,还有更多!

环顾四周并与Jon Skeet交谈,他向我指出了他写的关于记忆和字符串的文章.在那篇文章中,他提供了一个大小表:

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
Run Code Online (Sandbox Code Playgroud)

斯凯特先生接着说:

你可能会因为查看上面的数字而认为对象的"开销"在x86中是12个字节而在x64中是24个......但这并不完全正确.

还有这个:

  • 在x86中每个对象有8个字节的"基本"开销,在x64中每个对象有16个......因为我们可以在x86中存储Int32的"真实"数据并且仍然具有12的对象大小,同样我们可以存储x64中有两个真实数据的Int32,但仍然有一个x64对象.

  • "最小"大小分别为12个字节和24个字节.换句话说,你不能拥有只是开销的类型.注意"Empty"类如何占用与创建Object实例相同的大小...实际上有一些空余空间,因为CLR不喜欢在没有数据的对象上操作.(请注意,没有字段的结构也会占用空间,即使对于局部变量也是如此.)

  • x86对象填充到4字节边界; 在x64上,它是8个字节(就像之前一样)

最后Jon Skeet回答了在另一个问题中向他提出的一个问题(他回应了我向他展示的InformIT文章):

看起来你所指的文章只是从极限推断开销,这是 愚蠢的 IMO.

所以为了回答你的问题,我收集的实际开销是24字节,32字节的备用空间.