在.Net中触发垃圾收集的标准

时间:2010-04-19 21:47:53

标签: .net garbage-collection clr

我在.Net中遇到了一些关于垃圾收集的奇怪行为。

以下程序将非常快速地抛出OutOfMemoryException(在32位,2GB机器上不到一秒钟之后)。永远不会调用Foo终结器。

class Foo
{
    Guid guid = Guid.NewGuid();
    byte[] buffer = new byte[1000000];

    static Random rand = new Random();
    public Foo()
    {
        // Uncomment the following line and the program will run forever.
        // rand.NextBytes(buffer);
    }

    ~Foo()
    {
        // This finalizer is never called unless the rand.NextBytes
        // line in the constructor is uncommented.
    }

    static public void Main(string args[])
    {
        for (; ; )
        {
            new Foo();
        }
    }
}

如果取消注释rand.nextBytes行,它将无限运行,并定期调用Foo终结器。那是为什么?

我最好的猜测是,在前一种情况下,CLR或Windows VMM都懒得分配物理内存。永远不会写入缓冲区,因此永远不会使用物理内存。当地址空间用完时,系统崩溃。在后一种情况下,系统在地址空间不足之前耗尽物理内存,触发GC并收集对象。

然而,这是我没有得到的部分。假设我的理论是正确的,为什么地址空间不足时GC不会触发?如果我的理论不正确,那么真正的解释是什么?

1 个答案:

答案 0 :(得分:1)

代码在我的机器上以稳定的18MB运行,有或没有该行(XP SP3 x86,.Net 3.5 SP1,双核)

你机器上发生的事情可能就是当行被注释时,程序花费大部分时间进行分配,并设法在垃圾收集器线程有机会解除分配之前分配太多内存。取消注释该行时,程序花费的时间会少得多,因此在GC线程运行之前不能分配太多。

尝试用Thread.Sleep(0)替换注释行;如果它没有崩溃,我可能是正确的。


正如旁注,你不应该依赖终结器 - 当对象是GC时,甚至根本不能保证立即调用它。相反,在实际代码中实现IDisposable接口,并且只有在非常重要且Dispose()被调用时才使用终结器,即使程序员忘记了它(例如,释放)共享网络/文件资源等。)