LOH碎片 – 2015年更新
有很多关于.NET LOH的信息,并且已在各种文章中对其进行了解释。 然而,似乎有些文章缺乏一点精确性。
过时的信息
在Brian Rasmussen的回答(2009年),微软的项目经理 ,他说限制是85000字节。 他还告诉我们,有一个更加奇怪的double[]
案例,大小为1000个元素。 CLR团队成员Maoni Stephens(MSDN,2008)也提出了相同的85000限制。
在评论中,Brian Rasmussen变得更加精确,让我们知道它可以用85000字节的byte[]
–12字节来再现。
2013年更新
Mario Hewardt(“高级Windows调试”的作者)在2013年告诉我们,如果我们告诉它,.NET 4.5.1现在也可以压缩LOH。 由于默认情况下它处于关闭状态,因此问题仍然存在,除非您已经意识到这一点。
2015年更新
我不能再重现byte[]
示例了。 使用短蛮力算法,我发现我必须减去24(SOH中的byte[85000-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[10622]
在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构建。
如何解释变化?
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[]
了。
顺便说一句,我之前从未找到任何关于LOH碎片的工作演示,所以我自己写了一篇。 只需为x86编译以下代码并运行它。 它甚至包括一些调试提示。
编译为x64时,它将无法正常工作,因为Windows可能会增加页面文件的大小,因此后续分配20 MB内存可能会再次成功。
class Program { static IList small = new List(); static IList big = new List(); 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(); } } } }