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

Ken*_*nky 6 .net clr garbage-collection

我在.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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Blu*_*eft 1

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

您的机器上可能发生的情况是,当对该行进行注释时,程序将大部分时间都花在分配上,并在垃圾收集器线程有机会释放它之前设法分配了太多内存。当您取消注释该行时,程序花费的分配时间会少得多,因此在 GC 线程运行之前不能分配太多时间。

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


顺便说一句,您不应该依赖终结器 - 它不能保证在对象被 GC 时立即被调用,甚至根本不被调用。相反,在实际代码中实现接口,并且仅在调用极其重要的IDisposable情况下才使用终结器,即使程序员忘记了它(例如释放共享网络/文件资源等)Dispose()