c# - 循环中不可访问对象的垃圾收集

标签 c# .net garbage-collection

如果我有这样一个循环:

public class Foo {
     public Foo Foo;

     public Foo() {
     }
}

class Program {
    public static void Main(string[] args) {
         var foo = new Foo();
         long i = 0;
         while(i < Int64.MaxValue) {
             foo.Foo = new Foo();
             foo = foo.Foo;
             if(i % 10000 == 0)
                 GC.Collect();
             i++;
         }
         GC.Collect();
    }
}

直到循环退出,垃圾收集器才会清理父对象。这是为什么?一旦 foo 被重新分配,我看不出有任何方法可以从代码中引用它们,所以不应该清理它们吗?

在通过我设置的一些断点以确定发生这种情况后,我正在任务管理器中查看进程的内存使用情况。它在循环内不断上升(如果我将其设为无限,则上升到数 GB),但在循环退出并调用第二个 GC.Collect() 时立即下降。

最佳答案

这是一个稍微修改过的程序,可以更清楚地演示该行为:

class Foo
{
    public int Value;
    public Foo Next;

    public Foo(int value) { this.Value = value; Console.WriteLine("Created " + this.Value); }
    ~Foo() { Console.WriteLine("Finalized " + this.Value); }
}

class Program
{
    public static void Main(string[] args)
    {
        var foo = new Foo(0);
        for (int value = 1; value < 50; ++value)
        {
            foo.Next = new Foo(value);
            foo = foo.Next;
            if (value % 10 == 0)
            {
                Console.WriteLine("Collecting...");
                GC.Collect();
                Thread.Sleep(10);
            }
        }
        Console.WriteLine("Exiting");
    }
}

在 .NET 4.5 上,当我在 Debug模式下构建AND 目标任何 CPU 或 x86 时,我重现了您所看到的行为:在打印“Exiting”之前实例不会最终确定.但是当我在 Release模式目标 x64 中构建时(即使在 Debug模式下构建时),实例一旦无法访问就会完成:

Created 0
Created 1
Created 2
Created 3
Created 4
Created 5
Created 6
Created 7
Created 8
Created 9
Created 10
Collecting...
Finalized 9
Finalized 0
Finalized 8
Finalized 7
Finalized 6
Finalized 5
Finalized 4
Finalized 3
Finalized 2
Finalized 1
Created 11
Created 12
Created 13
...

为什么会这样?我想只有 CLR 专家才能确定地告诉我们,但这是我的猜测:行为取决于 JIT 编译器和优化器碰巧生成的机器代码的具体细节,这些细节因目标指令集以及您是否“重新以 Debug模式运行。 (此外,这些细节可能会在运行时的 future 版本中发生变化。)特别是在 x86/Debug 情况下,我认为第一个 Foo(0) 实例被隐藏在寄存器或堆栈变量中永远不会在方法的其余部分被覆盖;这个初始实例使整个链保持事件状态。在 x86/Release 和 x64 情况下,我认为由于 JIT 优化,相同寄存器或堆栈变量被每个实例重用,从而释放初始实例。

关于c# - 循环中不可访问对象的垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24153829/

相关文章:

c# - 使用绑定(bind)到类的单击事件的上下文

c# - Protobuf-net 序列化问题

c# - 如何强制编译器使用 ldc.i8 IL 指令而不是 ldc.i4 加 conv.i8?

.net 垃圾收集和托管资源

c# - 如何使用 Visual Studio 扩展创建绿色(或蓝色)波浪形装饰

C# 和 SQL 服务器 : execute stored procedure

.net - 如何将此巴科斯范式表达式转换为正则表达式 (.Net)?

c# - 如何使用内置的 .NET 功能检测输入的字符串字符语言?

c# - 将方法转换为静态方法时的内存使用

c# - Win32.DestroyIcon 与 Icon.Dispose