2010-04-19 44 views
6

我在.NET中遇到了一些关于垃圾回收的好奇行为。在.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行注释掉,它将运行循环往复,而富终结定期调用。这是为什么?

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

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

回答

1

的代码运行在我的机器上稳定的18MB,具有或不具有行(XP SP3的x86,净3.5 SP1,双核)

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

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


正如一个侧面说明,你不应该永远依靠终结 - 它不能保证被调用时立即对象是GC'ed,甚至都没有。相反,真正的代码实现IDisposable接口,并使用一个终结,只有当它是非常重要Dispose()被调用,即使程序员忘了(例如,释放网络共享/文件资源等)

+0

我我不确定你是否有正确的答案,但是你让我走上了一条有趣的轨道。我关闭了两台运行的虚拟PC(均为512MB),并获得了您的行为。当我加载VPC时,我得到了最初的崩溃行为。 – 2010-04-19 22:16:13

+0

在'GC'类中还有一些有趣的函数可以检出,但是在真实代码中这些函数永远不会被使用。 – 2010-04-19 22:18:04

+0

当VPC运行并且Thread.Sleep(0)就位时,程序将永久运行。现在问题发生了变化......为什么在分配失败时GC不会自动触发? – 2010-04-19 22:18:11