.NET垃圾收集器之谜 [英] .NET Garbage Collector mystery

查看:164
本文介绍了.NET垃圾收集器之谜的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的工作,我们曾与OutOfMemoryExceptions一个问题。我写了一段简单的code模仿一些行为,我已经结束了与下面的神秘面纱。看看这个简单的code这打击了,当它运行的内存。

 类节目
{
    私有静态无效的主要()
    {
        名单< byte []的>名单=新的名单,其中,byte []的>(200000);
        INT ITER = 0;

        尝试
        {
            为(;; ITER ++)
            {
                list.Add(新字节[10000]);
            }
        }
        赶上(OutOfMemoryException异常)
        {
            Console.WriteLine(迭代+ ITER);
        }
    }
}
 

在我的机器就结束了。

迭代:148008

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

  // ...
            为(;; ITER ++)
            {
                list.Add(新字节[10000]);

                如果(ITER%1000 == 0)
                    所以GC.Collect();
            }
            // ...
 

和惊喜:

迭代:172048

当我叫 GC.Collect的每10次迭代之后,我甚至得到了193716次。有两件怪事:

  1. 如何手动调用 GC.Collect的有这么严重的影响(多达分配的30%)?

  2. 到底能GC收集,当是没有丢失引用(我甚至preSET列表的能力)?

解决方案

垃圾收集过程的一部分是压制阶段。在这一阶段中,分配的存储器的块被四处移动以减少fragementation。当内存分配,它并不总是分配之后分配内存的最后一个块不放过。所以,你能获得更多一点儿,因为垃圾收集器是通过更好地利用可用空间腾出更多空间。

我想运行一些测试,但我的机器无法处理它们。试试这个,它会告诉 GC 来牵制内存中的对象,所以他们不在身边

感动

 字节[] B =新的字节[10000]
GCHandle.Alloc(B,GCHandleType.Pinned);
list.Add(B);
 

至于你的意见,当 GC 移动周围的事物,它不擦任何东西了,它只是更好地利用所有的内存空间。让我们尝试了简化。当您分配的字节数组的第一次,可以说,它得到下一次分配的字节数组插入内存从0点到10000,这是保证为未开始在10001,它可能会开始在10500.所以现在你有没有使用499字节,将不能使用你的应用程序。因此,当 GC 不密实,它将在10500阵列移至10001至能够使用额外的499个字节。再次,这是一路过来简化。

In my job we had a problem with OutOfMemoryExceptions. I've written a simple piece of code to mimic some behavior, and I've ended up with the following mystery. Look at this simple code which blows up when it runs out of memory.

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

On my machine it ended up with

Iterations: 148008

Then I added a GC.Collect call to the loop after each thousand iterations:

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

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

And surprise:

Iterations: 172048

When I called GC.Collect after each 10 iterations, I even got 193716 cycles. There are two strange things:

  1. How can a manual call to GC.Collect have such a severe impact (up to 30% more allocated)?

  2. What the hell can GC collect, when there're no "lost" references (I've even preset the List's capacity)?

解决方案

A part of the garbage collection process is the compacting phase. During this phase, blocks of allocated memory are moved around to reduce fragementation. When memory is allocated, it isn't always allocated right after the last chunk of allocated memory left off. So you are able to squeeze a bit more in because the garbage collector is making more room by making better use of the available space.

I am trying to run some tests, but my machine can't handle them. Give this a try, it will tell the GC to pin down the objects in memory so they aren't moved around

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

As for your comment, when the GC moves things around, it isn't wiping anything out, it is just making better use of all memory space. Lets try and over simplify this. When you allocate your byte array the first time, lets say it gets inserted in memory from spot 0 to 10000. The next time you allocate the byte array, it isn't guarenteed to start at 10001, it may start at 10500. So now you have 499 bytes that aren't being used, and won't be used by your application. So when the GC does compacting, it will move the 10500 array to 10001 to be able to use that extra 499 bytes. And again, this is way over simplified.

这篇关于.NET垃圾收集器之谜的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆