我很难理解垃圾收集器在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);
}
输出为True / True / True / False
在两个测试中,在我看来,在调用GC.Collect之前,堆上的对象没有root。但实际上,在测试1中,对象通过强制gc运行,而在测试2中却没有。那么,使用初始化器有什么神秘感吗? 我的猜测是,当使用初始化程序时,可能会有“一些额外的代码”,这将成为同一个对象的强根......
感谢。
答案 0 :(得分:5)
使用初始化程序时说
var a = new A {c=199}; --------> 1
编译器在堆栈上包含一个额外的引用,使对象通过GC 上述陈述(1)结果如下
var temp = new A() ;
temp.c=199;
var a=temp .
我认为这个临时变量使这个对象在GC期间生效。
请参阅此link
编辑:正如评论中 TomTom 所述。如果调试器正在运行,那么GC行为将被更改并且变量保持活动直到方法结束,直到最后一次使用。如果我错了,请纠正我
答案 1 :(得分:4)
显然,您正在运行Debug构建或附加调试器。垃圾收集器从即时编译器获取生命周期提示,它生成一个表,指示可以引用局部变量的代码段。垃圾收集器遍历由GC中断的执行方法的堆栈,并根据此表检查执行位置。并且在找到匹配时将引用计为有效。
如果代码是在Debug配置中构建的,或者在附加调试器时,抖动会修改此表并让变量保持活动状态直到方法体结束。这使得调试代码变得更加容易,您可以将局部变量放在监视表达式中,即使您超过不再使用该变量的点,它也会产生结果。
@Imposter发布的答案是正确的,隐藏的临时变量使A的第一个实例保持活动状态。并且垃圾收集器认为它有效,直到方法结束,因为您正在使用调试器。您的第二个a = null;
分配允许第二个实例被垃圾收集。
在生产中运行此代码时会发生什么,这是非常不同的。首先,抖动优化器将删除 a = null分配。它知道那些赋值没有有用的副作用,因此它不会为它们生成代码。非常不直观,最好的方法是采取以下步骤:
最后一个选项更改允许您继续使用调试器,而不会影响抖动生成代码的方式。现在临时变量将不再保留引用的第一个A实例,由抖动生成的表只会将其标记为存储方法中第一个语句的有效引用。运行你的程序,你会看到:
True
False
True
False
有了重要的新见解,即实际上不需要设置对null的引用,垃圾收集器足够聪明,不需要您提供帮助。
答案 2 :(得分:0)
我非常相信堆栈上的额外引用会产生GC Root。
让我们通过SOS调试来确认..
使用初始化程序时,
(before GC.Collect)
01bdc110 - object address of A.
!GCRoot 01bdc110
Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info.
Scan Thread 2920 OSTHread b68
ESP:20e960:Root: 01bdc110(GCTest.A)
**ESP:20ebb8:Root: 01bdc110(GCTest.A) -- extra stack reference generated by compiler**
ESP:20ebbc:Root: 01bdc110(GCTest.A)
ESP:20ebc4:Root: 01bdc110(GCTest.A)
Scan Thread 2404 OSTHread 964
(GC.Collect之后)
!GCRoot 01bdc110
Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info.
Scan Thread 2920 OSTHread b68
**ESP:20ebb8:Root: 01bdc110(GCTest.A) ----//Still remains**
ESP:20ebbc:Root: 01bdc110(GCTest.A)
Scan Thread 2404 OSTHread 964
确认未删除弱引用。
!GCRoot 01bdc210 //(address of weak reference)
Scan Thread 2920 OSTHread b68
**ESP:20e968:Root: 01bdc210(System.WeakReference) // Created by us**
ESP:20ebb4:Root: 01bdc210(System.WeakReference) // other in mscorlib
ESP:20ebc0:Root: 01bdc210(System.WeakReference) // other in mscorlib
Scan Thread 2404 OSTHread 964
2,没有初始化程序,
!GCRoot 01bdd4e0
Scan Thread 2920 OSTHread b68
ESP:20e960:Root: 01bdd4e0(GCTest.A)
ESP:20eba8:Root: 01bdd4e0(GCTest.A)
ESP:20ebc4:Root: 01bdd4e0(GCTest.A)
Scan Thread 2404 OSTHread 964
**No extra stack reference.**