有很多关于.NET LOH的信息,并且已在各种文章中对其进行了解释。然而,似乎有些文章缺乏一点精确性。
在Brian Rasmussen's answer (2009), program manager at Microsoft中,他说限制是85000字节。他还让我们知道,double[]
有一个更奇怪的案例,大小为1000个元素。 Maoni Stephens (MSDN, 2008), member of the CLR team表示相同的85000限制。
在评论中,Brian Rasmussen变得更加精确,让我们知道它可以用byte[]
85000字节--12字节进行复制。
Mario Hewardt (author of 'Advanced Windows Debugging') 在2013年告诉我们,如果我们告诉它,.NET 4.5.1现在也可以压缩LOH。由于默认情况下它处于关闭状态,因此问题仍然存在,除非您已经意识到这一点。
我无法重现byte[]
示例。使用短蛮力算法,我发现我必须减去24(SOH中byte[84999-24]
,LOH中byte[85000-24]
):
static void Main(string[] args)
{
int diff = 0;
int generation = 3;
while (generation > 0)
{
diff++;
byte[] large = new byte[85000-diff];
generation = GC.GetGeneration(large);
}
Console.WriteLine(diff);
}
我也无法重现double[]
语句。蛮力给我10622个元素作为边界(SOH中为double[10621]
,LOH中为double[10622]
):
static void Main(string[] args)
{
int size = 85000;
int step = 85000/2;
while (step>0)
{
double[] d = new double[size];
int generation = GC.GetGeneration(d);
size += (generation>0)?-step:step;
step /= 2;
}
Console.WriteLine(size);
}
即使我为较旧的.NET框架编译应用程序,也会发生这种情况。它也不依赖于Release或Debug构建。
如何解释变更?
答案 0 :(得分:4)
byte[]
示例中从12更改为24可以通过CPU架构从32位更改为64位来解释。在为x64或AnyCPU编译的程序中,.NET开销从2 * 4字节(4字节对象标题+ 4字节方法表)增加到2 * 8字节(8字节对象标题+ 8字节方法表)。此外,该数组的长度属性为4字节(32位),而8字节(64位)。
对于double[]
示例,只需使用计算器:85000字节/ 64位用于double类型= 10625项,已经关闭。考虑到.NET开销,结果是(85000字节 - 24字节)/每双8字节= 10622双精度。因此,double[]
不再需要特殊处理。
当编译为x64时,它将无法正常工作,因为Windows可能会增加页面文件的大小,因此后续分配20 MB内存可能会再次成功。
class Program
{
static IList<byte[]> small = new List<byte[]>();
static IList<byte[]> big = new List<byte[]>();
static void Main()
{
int totalMB = 0;
try
{
Console.WriteLine("Allocating memory...");
while (true)
{
big.Add(new byte[10*1024*1024]);
small.Add(new byte[85000-3*IntPtr.Size]);
totalMB += 10;
Console.WriteLine("{0} MB allocated", totalMB);
}
}
catch (OutOfMemoryException)
{
Console.WriteLine("Memory is full now. Attach and debug if you like. Press Enter when done.");
Console.WriteLine("For WinDbg, try `!address -summary` and `!dumpheap -stat`.");
Console.ReadLine();
big.Clear();
GC.Collect();
Console.WriteLine("Lots of memory has been freed. Check again with the same commands.");
Console.ReadLine();
try
{
big.Add(new byte[20*1024*1024]);
}
catch(OutOfMemoryException)
{
Console.WriteLine("It was not possible to allocate 20 MB although {0} MB are free.", totalMB);
Console.ReadLine();
}
}
}
}