【问题标题】:Collect objects still in scope - GC.Collect收集仍在范围内的对象 - GC.Collect
【发布时间】:2011-09-19 11:36:13
【问题描述】:

我已经阅读了这篇文章:http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048149.aspx,老实说,我并不了解每一个细节。据我了解,即使我没有将 c 设置为 null,也应该在下面的代码中收集 c。另一件事是,只要我们在同一函数的范围内,在 foreach 期间发生的分配似乎就不会被释放。 (见下例)

class Program
{
    public class SomeClass
    {
        public byte[] X;

        public SomeClass()
        {
            X = new byte[1024 * 1024 * 100];
            X[155] = 10;
        }
    }

    static void Main()
    {
        Console.WriteLine("Memory: " + GC.GetTotalMemory(false));

        SomeClass c;
        c = new SomeClass();

        Console.WriteLine("Memory: " + GC.GetTotalMemory(false));
        GC.Collect();
        Console.WriteLine("Memory: " + GC.GetTotalMemory(true));
        Console.ReadKey();

        /*
         * Output:
         * 
         * Memory: 186836
         * Memory: 105044468
         * Memory: 104963676
         * 
         */
    }
}

EDIT 第一个示例的解决方案:调试模式(不在调试器中运行,甚至在编译模式中运行)。如果我使用 Release,它将按预期工作:即使没有设置为 null,也会收集 c。第二个例子同样适用。

第二个例子

static void Main(string[] args)
{
    Console.WriteLine("Startup Memory: " + GC.GetTotalMemory(false));

    var obj = BOObject.Get();

    Console.WriteLine("Fetched Object: " + GC.GetTotalMemory(false));
    GC.Collect();
    Console.WriteLine("Fetched Object (Collected): " + GC.GetTotalMemory(false));

    foreach (var node in obj.Traverse())
    {
        string name = node.Name;
    }

    Console.WriteLine("Traversed: " + GC.GetTotalMemory(false));
    GC.Collect();
    Console.WriteLine("Traversed (collected): " + GC.GetTotalMemory(false));

    obj = null;

    GC.Collect();
    Console.WriteLine("collected: " + GC.GetTotalMemory(true));
    Console.Read();
}

输出:

启动内存:193060 获取:8972464 获取(收集):5594308 遍历:272553096 遍历(收集):269564660 收集到:269564048

如果我将 foreach 循环放在另一个函数中并调用此函数,.Collect 调用后使用的内存约为 5800000。那么为什么当我在同一个函数中使用 foreach 循环时没有收集垃圾?

【问题讨论】:

    标签: c# .net garbage-collection


    【解决方案1】:

    在 Raymond 的文章的 cmets 和 Yun Jin 的 MSDN 文章here 中提到

    事实上,对于可调试的代码,JIT 将每个变量的生命周期延长到 函数结束。

    因此,您的集合不会像您发现的那样在调试模式下被收集,以及为什么会在发布模式下被收集。

    【讨论】:

      【解决方案2】:

      垃圾收集器的行为不是确定性的,不应期望是确定性的。不要依赖 GC。

      你在问为什么 GC没有在给定的时间点收集你的垃圾。更好的问题是为什么它在给定时间点收集。

      【讨论】:

      • msdn.microsoft.com/en-us/library/xe0c2357.aspx 这不是确定性的,但您可以强制收集尝试收集未使用的对象。 GC.GetTotalMemory(true) 等待当前集合完成。所以至少应该释放一些数据。每次尝试的数量不同是可以的,但预计不会有 200 MB 的差异......
      • @Malone 它不是确定性的。所以“所以至少应该释放一些数据”这句话不适用。为了能够说它必须是确定性的。
      猜你喜欢
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 2012-08-03
      • 2011-10-22
      相关资源
      最近更新 更多