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中却没有.那么,使用初始化器有什么神秘感吗?我的猜测是,当使用初始化程序时,可能会有"一些额外的代码",它将成为同一个对象的强根.....
谢谢.
使用初始化程序时说
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行为将被更改并且变量保持活动直到方法结束,直到最后一次使用.如果我错了,请纠正我
显然,您正在运行调试版本或附加了调试器。垃圾收集器从即时编译器获取生命周期提示,它生成一个表,指示在代码的哪些部分可以引用局部变量。垃圾收集器会遍历被 GC 中断的执行方法的堆栈,并根据该表检查执行位置。当找到匹配时,将引用视为有效。
如果代码是在调试配置中构建的或者附加了调试器,则抖动会修改此表并让变量保持活动状态,直到方法主体结束。这使得调试代码变得更加容易,您可以将局部变量放入监视表达式中,即使您单步越过不再使用该变量的点,它也会产生结果。
@Imposter 发布的答案是正确的,隐藏的临时变量使 A 的第一个实例保持活动状态。并且垃圾收集器在方法结束之前都认为它有效,因为您正在使用调试器。您的第二个a = null;分配允许对第二个实例进行垃圾收集。
当您在生产中运行此代码时,真正发生的情况是非常不同的。其一,抖动优化器将删除a = null 分配。它知道这些分配没有有用的副作用,因此它不会为它们生成任何代码。非常不直观,看到这一点的最佳方法是采取以下步骤:
最后一个选项更改允许您继续使用调试器,而不会影响抖动生成代码的方式。现在临时变量将不再保留引用的 A 的第一个实例,抖动生成的表只会将其标记为存储方法中第一个语句的有效引用。运行你的程序,你会看到:
True
False
True
False
Run Code Online (Sandbox Code Playgroud)
有了一个重要的新见解,即设置对 null 的引用实际上并不是必需的,垃圾收集器足够聪明,不需要您的帮助。