c# – .NET垃圾收集器神秘

在我的工作中,我们有一个OutOfMemoryExceptions的问题。我写了一段简单的代码来模仿一些行为,我结束了以下的奥秘。看看这个简单的代码,当它耗尽内存爆炸。

class Program
{
    private static void Main()
    {
        List<byte[]> list = new List<byte[]>(200000);
        int iter = 0;

        try
        {
            for (;;iter++)
            {
                list.Add(new byte[10000]);
            }
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("Iterations: " + iter);
        }
    }
}

在我的机器上它最终

迭代:148008

然后我在每一千次迭代后添加一个GC.Collect调用到循环:

            //...
            for (;;iter++)
            {
                list.Add(new byte[10000]);

                if (iter % 1000 == 0)
                    GC.Collect();
            }
            //...

和惊喜:

迭代:172048

当我在每次迭代10次后调用GC.Collect,我甚至得到193716个周期。有两个奇怪的事情:

>如何手动调用GC.Collect有这么严重的影响(多达30%的分配)?
>当没有“丢失”引用(我甚至预置了列表的容量)时,GC可以收集什么?

垃圾收集过程的一部分是压缩阶段。在该阶段期间,分配的存储器块被移动以减少分片。当存储器被分配时,它不总是在分配的存储器的最后一个块之后被分配。所以你能够挤压一点,因为垃圾收集器通过更好地利用可用空间,使更多的空间。

我试图运行一些测试,但我的机器不能处理它们。给这个尝试,它会告诉GC固定在内存中的对象,所以他们不移动

byte[] b = new byte[10000];
GCHandle.Alloc(b, GCHandleType.Pinned);
list.Add(b);

至于你的评论,当GC移动的东西,它不是擦除任何东西,它只是更好地利用所有的内存空间。让我们试着简化这个。当你第一次分配你的字节数组,我们说它被插入到内存从点0到10000.下一次分配字节数组,它不是肯定从10001开始,它可能从10500开始。所以现在您有499字节未使用,并且不会被您的应用程序使用。因此,当GC执行压缩时,它会将10500阵列移动到10001,以便能够使用额外的499字节。再次,这是方式过于简化。

http://stackoverflow.com/questions/1729289/net-garbage-collector-mystery

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – .NET垃圾收集器神秘