LOH碎片化- 2015年更新

本文关键字:2015年 更新 碎片 LOH | 更新日期: 2023-09-27 17:50:20

有很多关于. net LOH的信息,在各种文章中都有解释。然而,有些文章似乎不够精确。

过时的信息

在微软项目经理Brian Rasmussen(2009)的回答中,他说限制是85000字节。他还让我们知道,还有一个更奇怪的double[],它的大小是1000个元素。CLR团队成员Maoni Stephens (MSDN, 2008)也提出了同样的85000限制。

在评论中,Brian Rasmussen变得更加精确,让我们知道它可以用85000字节- 12字节的byte[]复制。

2013

更新

Mario Hewardt(《Advanced Windows Debugging》的作者)在2013年告诉我们。net 4.5.1现在也可以压缩LOH,如果我们告诉它这样做的话。因为它在默认情况下是关闭的,所以问题仍然存在,除非您已经意识到它。

2015

更新

我不能再复制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框架中编译应用程序,也会发生这种情况。它也不依赖于发布或调试版本。

如何解释这些变化?

LOH碎片化- 2015年更新

byte[]示例中从12到24的变化可以通过CPU架构从32位到64位的变化来解释。在为x64或AnyCPU编译的程序中,. net开销从2*4字节(4字节Object Header + 4字节Method Table)增加到2*8字节(8字节Object Header + 8字节Method Table)。此外,该数组的长度属性为4字节(32位),而不是8字节(64位)。

对于double[]的例子,只需使用计算器:85000字节/64位的双类型= 10625项,这已经很接近了。考虑到。net的开销,结果是(85000字节- 24字节)/8字节/双精度体= 10622双精度体。所以double[]不再有特殊的处理。

顺便说一句,我以前从来没有发现过LOH碎片的任何工作演示,所以我自己写了一个。只需为x86编译下面的代码并运行它。它甚至包括一些调试提示。

当编译为x64时,它将不能很好地工作,因为Windows可能会增加页面文件的大小,因此后续分配20mb内存可能会再次成功。

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();
            }
        }
    }
}