C#GC.Collect如果使用实例构造函数初始化程序构造它,则不会销毁该对象

ale*_*231 12 .net c# garbage-collection weak-references

可能重复:
使用对象初始化程序的复活差异

我很难理解垃圾收集器在C#中是如何工作的(我使用的是2012,所以c#4.5).这是我的示例代码:

    public class A
    {
        public int c;
        public A(){}
        public A(int pC)
        {
            c = pC;
        }
    }

    public static void Main()
    {
        // Test 1
        var a = new A {c=199};
        var aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
        //            Console.WriteLine(GC.GetGeneration(aRef.Target)); //output 1

        // Test 2
        a = new A (200);
        aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
    }
Run Code Online (Sandbox Code Playgroud)

输出为True/True/True/False

在我看来,在两个测试中,在调用GC.Collect之前,堆上的对象没有root.但实际上,在测试1中,对象通过强制gc运行,而在测试2中却没有.那么,使用初始化器有什么神秘感吗?我的猜测是,当使用初始化程序时,可能会有"一些额外的代码",它将成为同一个对象的强根.....

谢谢.

Imp*_*ter 5

使用初始化程序时说

 var a = new A {c=199}; --------> 1
Run Code Online (Sandbox Code Playgroud)

编译器在堆栈上包含一个额外的引用,使对象通过GC.
上述陈述(1)结果如下

 var temp = new A() ;
  temp.c=199;
  var a=temp . 
Run Code Online (Sandbox Code Playgroud)

我认为这个临时变量使这个对象在GC期间保持活着.

请参阅此链接

编辑:正如TomTom在评论中提到的那样.如果调试器正在运行,那么GC行为将被更改并且变量保持活动直到方法结束,直到最后一次使用.如果我错了,请纠正我

  • 可能也是一个调试问题,其中变量保持活着直到方法结束,直到最后一次使用. (4认同)

Han*_*ant 4

显然,您正在运行调试版本或附加了调试器。垃圾收集器从即时编译器获取生命周期提示,它生成一个表,指示在代码的哪些部分可以引用局部变量。垃圾收集器会遍历被 GC 中断的执行方法的堆栈,并根据该表检查执行位置。当找到匹配时,将引用视为有效。

如果代码是在调试配置中构建的或者附加了调试器,则抖动会修改此表并让变量保持活动状态,直到方法主体结束。这使得调试代码变得更加容易,您可以将局部变量放入监视表达式中,即使您单步越过不再使用该变量的点,它也会产生结果。

@Imposter 发布的答案是正确的,隐藏的临时变量使 A 的第一个实例保持活动状态。并且垃圾收集器在方法结束之前都认为它有效,因为您正在使用调试器。您的第二个a = null;分配允许对第二个实例进行垃圾收集。

当您在生产中运行此代码时,真正发生的情况是非常不同的。其一,抖动优化器将删除a = null 分配。它知道这些分配没有有用的副作用,因此它不会为它们生成任何代码。非常不直观,看到这一点的最佳方法是采取以下步骤:

  • 从代码中删除 a = null 赋值
  • 使用 Build + Configuration Manager 切换到发布配置
  • 使用“工具+选项”、“调试”、“常规”,取消选中“抑制模块加载时的 JIT 优化”选项。

最后一个选项更改允许您继续使用调试器,而不会影响抖动生成代码的方式。现在临时变量将不再保留引用的 A 的第一个实例,抖动生成的表只会将其标记为存储方法中第一个语句的有效引用。运行你的程序,你会看到:

True
False
True
False
Run Code Online (Sandbox Code Playgroud)

有了一个重要的新见解,即设置对 null 的引用实际上并不是必需的,垃圾收集器足够聪明,不需要您的帮助。