class Program
{
static void Main(string[] args)
{
var test = new Test { Name = "One" };
var test2 = new Test { Name = "Two" };
var weak = new WeakReference<TestRef>(new TestRef(test, test2));
var weak2 = new WeakReference<TestRef>(new TestRef(test) { Test2 = test2 });
GC.Collect();
TestRef tref;
TestRef tref2;
var @is = weak.TryGetTarget(out tref); //FALSE
var @is2 = weak2.TryGetTarget(out tref2); //TRUE
}
}
class Test
{
public Test()
{ }
public string Name { get; set; }
}
class TestRef
{
public TestRef(Test test)
{ Test = test; }
public TestRef(Test test, Test test2)
{
Test = test;
Test2 = test2;
}
public Test Test { get; set; }
public Test Test2 { get; set; }
}
在此示例中,如果在 GC集合对象从堆中删除之后,
答案 0 :(得分:0)
出现“问题”是因为您对C#语言规范未强制要求的垃圾收集器行为做出了假设。特别是,您假设传递给WeakReference
构造函数的每个对象都有资格在下一行进行垃圾回收。这不是一个正确的假设;实际上,C#编译器实际上会产生两种不同的行为,具体取决于你是否启用了优化,并且两者都不比另一种更正确。
注意:使用Roslyn CTP for Visual Studio 2013和ILSpy 2.2.0.1706进行了以下评估。
启用优化后,中间值实际存储在专门为其创建的临时局部变量中。特定行是IL_0042: stloc.s 9
行,它将对象存储在局部变量[9]
中。使用后,此变量未设置为null
,因此引用实际上会阻止对象在Main
方法返回之前收集垃圾。
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 112 (0x70)
.maxstack 2
.locals init (
[0] class Test test,
[1] class Test test2,
[2] class [mscorlib]System.WeakReference`1<class TestRef> weak,
[3] class [mscorlib]System.WeakReference`1<class TestRef> weak2,
[4] class TestRef tref,
[5] class TestRef tref2,
[6] bool is,
[7] bool is2,
[8] class Test,
[9] class TestRef
)
IL_0000: nop
IL_0001: newobj instance void Test::.ctor()
IL_0006: stloc.s 8
IL_0008: ldloc.s 8
IL_000a: ldstr "One"
IL_000f: callvirt instance void Test::set_Name(string)
IL_0014: nop
IL_0015: ldloc.s 8
IL_0017: stloc.0
IL_0018: newobj instance void Test::.ctor()
IL_001d: stloc.s 8
IL_001f: ldloc.s 8
IL_0021: ldstr "Two"
IL_0026: callvirt instance void Test::set_Name(string)
IL_002b: nop
IL_002c: ldloc.s 8
IL_002e: stloc.1
IL_002f: ldloc.0
IL_0030: ldloc.1
IL_0031: newobj instance void TestRef::.ctor(class Test, class Test)
IL_0036: newobj instance void class [mscorlib]System.WeakReference`1<class TestRef>::.ctor(!0)
IL_003b: stloc.2
IL_003c: ldloc.0
IL_003d: newobj instance void TestRef::.ctor(class Test)
IL_0042: stloc.s 9
IL_0044: ldloc.s 9
IL_0046: ldloc.1
IL_0047: callvirt instance void TestRef::set_Test2(class Test)
IL_004c: nop
IL_004d: ldloc.s 9
IL_004f: newobj instance void class [mscorlib]System.WeakReference`1<class TestRef>::.ctor(!0)
IL_0054: stloc.3
IL_0055: call void [mscorlib]System.GC::Collect()
IL_005a: nop
IL_005b: ldloc.2
IL_005c: ldloca.s tref
IL_005e: callvirt instance bool class [mscorlib]System.WeakReference`1<class TestRef>::TryGetTarget(!0&)
IL_0063: stloc.s is
IL_0065: ldloc.3
IL_0066: ldloca.s tref2
IL_0068: callvirt instance bool class [mscorlib]System.WeakReference`1<class TestRef>::TryGetTarget(!0&)
IL_006d: stloc.s is2
IL_006f: ret
} // end of method Program::Main
启用优化后,您可以看到中间值仅存储在堆栈中;没有stloc
指令用于将其放在局部变量中。相反,行dup
上的IL_0033
指令更有效地提供了该功能。运行此代码时,垃圾收集器很可能会收集该对象,因为它在调用GC.Collect
时不再是线程堆栈的一部分。
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 86 (0x56)
.maxstack 4
.locals init (
[0] class Test test2,
[1] class [mscorlib]System.WeakReference`1<class TestRef> weak,
[2] class TestRef tref,
[3] class TestRef tref2
)
IL_0000: newobj instance void Test::.ctor()
IL_0005: dup
IL_0006: ldstr "One"
IL_000b: callvirt instance void Test::set_Name(string)
IL_0010: newobj instance void Test::.ctor()
IL_0015: dup
IL_0016: ldstr "Two"
IL_001b: callvirt instance void Test::set_Name(string)
IL_0020: stloc.0
IL_0021: dup
IL_0022: ldloc.0
IL_0023: newobj instance void TestRef::.ctor(class Test, class Test)
IL_0028: newobj instance void class [mscorlib]System.WeakReference`1<class TestRef>::.ctor(!0)
IL_002d: stloc.1
IL_002e: newobj instance void TestRef::.ctor(class Test)
IL_0033: dup
IL_0034: ldloc.0
IL_0035: callvirt instance void TestRef::set_Test2(class Test)
IL_003a: newobj instance void class [mscorlib]System.WeakReference`1<class TestRef>::.ctor(!0)
IL_003f: call void [mscorlib]System.GC::Collect()
IL_0044: ldloc.1
IL_0045: ldloca.s tref
IL_0047: callvirt instance bool class [mscorlib]System.WeakReference`1<class TestRef>::TryGetTarget(!0&)
IL_004c: pop
IL_004d: ldloca.s tref2
IL_004f: callvirt instance bool class [mscorlib]System.WeakReference`1<class TestRef>::TryGetTarget(!0&)
IL_0054: pop
IL_0055: ret
} // end of method Program::Main