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

标签 .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 不触发?如果我的理论不正确,那么真正的解释是什么?

最佳答案

代码在我的机器上以稳定的 18MB 运行,不管有没有这条线(XP SP3 x86、.Net 3.5 SP1、双核)。

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

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

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

关于.net - .Net 中触发垃圾收集的标准,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2671161/

相关文章:

.net - 如何处置工厂模式创建的对象

jvm - 为什么 CLR 会溢出 Int32.MaxValue -> Single -> Int32,而 JVM 不会?

.net - DotNet 2.0 或 3.5、IE、XSL 和 XSL-FO 功能是什么?

c# - 如何在区域设置中显示 0 值的日期?

c# - System.Net.Mail 创建无效的电子邮件和 eml 文件?在主机名中插入额外的点

.net - 注册 ADO.NET 提供程序

java - 为什么 WeakHashMap 在 GC 后对值有强引用?

java - 如何判断其中一代堆是否已满?

f# - 如何在F#中创建具有out参数的成员

.net - 以编程方式从现有程序集中删除自定义属性