为什么这个对象没有被垃圾回收?

6
在下面的代码段中,我想知道为什么在函数调用后testvectors没有被收集。我看到内存使用量上升到270Mb,然后就一直保持在那里。
这个函数是直接从Main调用的。
private static void increaseMemoryUsage()
{
    List<List<float>> testvectors = new List<List<float>>();
    int vectorNum = 250 * 250;
    Random rand = new Random();

    for (int i = 0; i < vectorNum; i++)
    {
        List<Single> vec = new List<Single>();

        for (int j = 0; j < 1000; j++)
                {
            vec.Add((Single)rand.NextDouble());
        }
        testvectors.Add(vec);
    }
}

定义“永远”,以及如何检查它? - user395760
仅通过观察内存消耗无法判断垃圾回收情况。尝试调用GC.collect或其它等效方法并检查发生了什么。 - Queequeg
永久循环大约需要30分钟...在任务管理器中检查。 - Johnyy
2
@Johnyy 我怀疑如果你再加把劲,然后尝试为其他事情使用大量内存,它就会。 - Joe
定义“永远”,以及如何验证它? OP 解决了停机问题。 - undefined
显示剩余4条评论
6个回答

9
不要将垃圾回收与引用计数混淆。内存在运行时决定释放,而不总是在不再被引用时释放。引用如下:
垃圾回收发生在以下条件之一为真时: - 系统具有较低的物理内存。 - 托管堆上分配对象使用的内存超过可接受的阈值。这意味着托管堆上已超出可接受的内存使用阈值。此阈值会随进程运行而不断调整。 - 调用GC.Collect方法。在几乎所有情况下,您都不必调用此方法,因为垃圾回收器会持续运行。此方法主要用于特殊情况和测试。
如果您感兴趣,请阅读此内容:

http://msdn.microsoft.com/en-us/library/0xy59wtx.aspx


5

垃圾回收器可以在它想要的时候运行。这可能会晚得多。在最后一个引用消失后,没有义务立即释放一些内存。你的数组将在下一个Gen2集合中被收集。

除非你不断地分配内存,否则它很可能在函数返回后永远不会运行。

你可以使用 GC.Collect() 手动触发垃圾回收,但这通常是不鼓励的。


让GC在感觉合适的时候运行(而不是强制使用GC.Collect())几乎总是正确的选择。系统通常比您更了解对象何时不再被引用以及如何和何时回收资源以实现最佳性能,而您的强制执行只会导致系统在许多情况下犹豫不决并变得更慢。一个对象必须立即被回收的情况非常罕见。 - cHao
2
系统拥有的信息比程序员少。例如,它无法知道您刚刚删除了200MB数据的最后一个引用。因此,在这种情况下手动调用可能是合理的。不幸的是,没有一种好的方法告诉垃圾回收器您不再需要大量内存,而不强制其运行。 - CodesInChaos
1
可能。也许。系统对于特定对象的信息较少,但总体上有更多信息。如果它知道200MB数据的另一个引用,那么你所做的只是让这些内容在下一次垃圾回收中幸存下来(并有可能晋升到下一代)。因此,如果你要强制执行它,你最好非常确定你是正确的。 - cHao

3

当我运行这个程序时,我发现相反的结果:

 class Program
    {
        static void Main(string[] args)
        {
            increaseMemoryUsage();

            Console.WriteLine("Hit enter to force GC");
            Console.ReadLine();

            GC.Collect();

            Console.ReadLine();
        }

        private static void increaseMemoryUsage()
        {
            var testvectors = new List<List<float>>();
            const int vectorNum = 250 * 250;
            var rand = new Random();

            for (var i = 0; i < vectorNum; i++)
            {
                var vec = new List<Single>();

                for (var j = 0; j < 1000; j++)
                    vec.Add((Single)rand.NextDouble());

                testvectors.Add(vec);
            }
        }    
    }

是的,GC.collect确实会减少内存占用。然而,不鼓励频繁使用GC.collect...所以。 - Johnyy
正确,这个想法是你不需要涉及GC,这就是你的问题的答案;当引用超出范围时,内存并没有被立即回收。我的观点是对象可供收集,并将在CLR认为合适时进行收集。 - Myles McDonnell

0

很可能,你的测试向量正在晋升到大对象堆(LOH),然后在Gen0收集期间不会被回收。

这里有一个好链接here


0

垃圾回收是非确定性的。它完全由GC决定何时进行垃圾回收。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接