所以,我有这个非常有用的精彩程序:
static void Main(string[] args)
{
new Dictionary<int,int>(10000000);
while (true)
{
System.Threading.Thread.Sleep(1000);
}
}
这甚至不会产生编译器的任何警告,这是令人惊讶的。
运行它会分配一块内存。如果我运行多个副本,我最终会达到无法启动的程度,因为我已经没有内存了。
那么这里发生了什么?
答案 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); } }
这个应该现在可以将内存留给垃圾收集