为什么这个内存没有得到清理,或者根本没有分配?

时间:2010-07-22 21:29:14

标签: c# garbage-collection

所以,我有这个非常有用的精彩程序:

static void Main(string[] args)
{
    new Dictionary<int,int>(10000000);

    while (true)
    {
        System.Threading.Thread.Sleep(1000);
    }
}

这甚至不会产生编译器的任何警告,这是令人惊讶的。

运行它会分配一块内存。如果我运行多个副本,我最终会达到无法启动的程度,因为我已经没有内存了。

  1. 为什么垃圾收集器不会清理内存,而是让系统进入没有足够内存用于新进程的状态?
  2. 哎呀,为什么不优化内存分配?它永远不会被任何东西引用!
  3. 那么这里发生了什么?

4 个答案:

答案 0 :(得分:6)

垃圾收集器是非确定性的,并且响应内存压力。如果没有任何内存需要,可能暂时不会收集。它无法优化new,因为这会改变您的代码:构造函数可能有副作用。此外,在调试中,它甚至更有可能决定不收集。

在发布/优化版本中,当有充分理由时,我会期望在某些时候收集它。还有GC.Collect,但除了极端情况或某些分析要求外,通常应该避免这种情况。

作为“为什么” - GC“代”之间的GC行为存在差异;你在“大对象堆”(LOH)上有一些大数组。这个LOH是非常昂贵的继续检查,这可以进一步解释为什么它是如此不情愿。

答案 1 :(得分:3)

我的猜测是隐藏的Gen0集合正在进行中。

这是我的测试程序:

static void Main(string[] args)
{
    new Dictionary<int, int>(10000000);
    Thread.Sleep(5000);

    int x = 1; // or 0;
    int i = 0;
    while (true)
    {
        object o = ++i;
        Thread.Sleep(x);
    }
}

当系统执行Sleep(1)时,系统必须认为这是仅在Gen0上快速隐藏GC的好时机。所以'object o = ++ i'语句从不对Gen0施加压力,也从不触发GC集合,因此永远不会释放字典。

Sleep(1) http://www.freeimagehosting.net/uploads/6fad1952e0.png

将x更改为0.现在,这个隐藏的GC不会发生,并且事情按预期工作,使用'object o = ++ i'语句导致收集字典。

Sleep(0) http://www.freeimagehosting.net/uploads/f285b8acdb.png

答案 2 :(得分:0)

答案 3 :(得分:-1)

我不知道这个是事实,但我猜是因为即使你没有创建对新词典的引用,它也被链接到当时的本地范围,你的程序永远不会离开。要检查是否是这种情况,只需在内部范围内创建字典,您可以在开始循环之前离开,例如。

static void Main(string[] args)
{
    {
        new Dictionary(10000000);
    }

    while (true)
    {
        System.Threading.Thread.Sleep(1000);
    }
}

这个应该现在可以将内存留给垃圾收集