我在.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不会触发?如果我的理论不正确,那么真正的解释是什么?
答案 0 :(得分:1)
代码在我的机器上以稳定的18MB运行,有或没有该行(XP SP3 x86,.Net 3.5 SP1,双核)。
你机器上发生的事情可能就是当行被注释时,程序花费大部分时间进行分配,并设法在垃圾收集器线程有机会解除分配之前分配太多内存。取消注释该行时,程序花费的时间会少得多,因此在GC线程运行之前不能分配太多。
尝试用Thread.Sleep(0)
替换注释行;如果它没有崩溃,我可能是正确的。
正如旁注,你不应该依赖终结器 - 当对象是GC时,甚至根本不能保证立即调用它。相反,在实际代码中实现IDisposable
接口,并且只有在非常重要且Dispose()
被调用时才使用终结器,即使程序员忘记了它(例如,释放)共享网络/文件资源等。)