在我的64位计算机上,这个C#代码有效:
new byte[2L * 1024 * 1024 * 1024 - 57]
但是这个会引发OutOfMemoryException
:
new byte[2L * 1024 * 1024 * 1024 - 56]
为什么?
据我所知,托管对象的最大大小为2 GB,而我正在创建的数组对象包含的内容超过了我想要的字节数。也就是说,同步块编号有4个字节(或8?),MethodTable参考有8个字节,数组大小有4个字节。这是24个字节,包括填充,所以为什么我不能分配一个2G-24字节的数组?最大尺寸是否完全 2 GB?如果是这种情况,2 GB的剩余部分用于什么?
注意:我实际上不需要分配一个包含200万字节的数组。即使我这样做,56个字节的开销可以忽略不计。我可以使用自定义BigArray<T>
轻松解决限制。
答案 0 :(得分:19)
您需要56个字节的开销。最大尺寸实际上是2,147,483,649-1 减去 56。这就是你的减去 57工作而减去 56的原因。
正如Jon Skeet所说:
然而,实际上,我没有 相信任何实现支持 如此巨大的数组。 CLR有一个 每个对象限制有点短2GB, 所以即使一个字节数组实际上也不行 有2147483648个元素。一点 实验表明,在我的盒子上, 你可以创建的最大阵列是 新字节[2147483591]。 (那就是 64位.NET CLR; Mono的版本 我已经安装了扼流圈。)
有关同一主题,请参阅此InformIT article。它提供以下代码来演示最大的大小和开销:
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千兆字节。
修改强>
但等等,还有更多!
环顾四周并与Jon Skeet交谈,他向我指出了他在Of memory and strings上写的一篇文章。在那篇文章中,他提供了一个大小表:
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中的字节和x64中的24 ...但是 那不太对劲。
和此:
在x86中每个对象有8个字节的“基本”开销,每个16个 x64中的对象......鉴于我们可以 在x86中存储Int32的“真实”数据 并且对象大小仍为12, 同样我们可以存储两个Int32 x64中的实际数据仍然有一个 x64的对象。
分别有12个字节和24个字节的“最小”大小。在 换句话说,你不能拥有一个类型 这只是开销。请注意如何 “Empty”类占用相同的值 大小为创建实例 对象......实际上有一些 备用房间,因为CLR没有 喜欢在没有的对象上操作 数据。 (注意没有的结构 字段占用空间,即使是 局部变量。)
x86对象填充到4字节边界;在x64上它是8个字节 (和以前一样)
最后,Jon Skeet在另一个问题中对question I asked of him作出了回应(他回应了我向他展示的InformIT article):
看起来像你的文章 指的是推断开销 只是从极限,这是 愚蠢 IMO。
所以为了回答你的问题,实际的开销是 24字节, 32字节的备用空间,来自我收集的内容。
答案 1 :(得分:3)
有一件事是肯定的是你不能有奇数个字节,它通常是本机字大小的倍数,在64位进程上是8字节。所以你可以在数组中添加另外7个字节。
答案 2 :(得分:2)
您实际上可以在 .net 源代码中找到明确设置和验证的此限制,它提供了一些有关这样做的原因(未来高级范围检查消除的有效实现,以及字节情况下的向后兼容性):< /p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>