【问题标题】:Criteria for triggering garbage collection in .Net.Net 中触发垃圾收集的标准
【发布时间】:2010-04-19 21:47:52
【问题描述】:

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

【问题讨论】:

    标签: .net garbage-collection clr


    【解决方案1】:

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

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

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


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

    【讨论】:

    • 我不确定你的答案是否正确,但你让我走上了一条有趣的道路。我关闭了我正在运行的两台虚拟 PC(均为 512MB),我明白了你的行为。当我重新加载 VPC 时,我得到了原始的崩溃行为。
    • GC 类中还有一些有趣的函数需要检查,但同样,在实际代码中这些函数永远不应该使用。
    • 随着 VPC 的运行和 Thread.Sleep(0) 的就位,程序将永远运行。现在问题转移了……为什么 GC 不会在分配失败时自动触发?
    • @Kennet:GC 也需要大量内存来完成它的工作。当没有没有内存可以分配时发生分配失败(来自操作系统),即使是 GC。
    猜你喜欢
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 2013-07-03
    相关资源
    最近更新 更多