c# - LOH 碎片 - 2015 年更新

标签 c# .net debugging out-of-memory windbg

有很多关于 .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 变得更加准确,让我们知道它可以用 85000 字节 - 12 字节的 byte[] 重现。

2013年更新

Mario Hewardt (author of '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 框架编译应用程序,也会发生这种情况。它也不依赖于 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 bytes/64 bit for the double type = 10625 items,这已经很接近了。考虑到 .NET 开销,结果是(85000 字节 - 24 字节)/每个 double 8 个字节 = 10622 double 。因此,不再对 double[] 进行特殊处理。

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

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

关于c# - LOH 碎片 - 2015 年更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30361184/

相关文章:

java - 无法在 IntelliJ 15 中添加断点

c# - 使用 Microsoft Bot Framework 时如何使用 CSS 设置聊天窗口的样式

c# - 隐式强制转换对委托(delegate)类型推断的意外影响

c# - 在 Ninject 中注入(inject)接口(interface)数组

c# - 不理解 RunInstaller 属性

c# - 检查两个对象的字段中是否包含相同的数据

javascript - 如何有效调试缩小后的 JS 文件?

C程序计算数组的长度

c# - Java 和 C# 客户端无法通过安全的 DataStax Enterprise 3.1 服务器进行身份验证

c# - .net c# asp.net 使文本框文本的一部分加粗