垃圾收集和对象初始化程序

时间:2014-09-11 11:56:25

标签: c# garbage-collection

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集合对象从堆中删除之后, 使用构造函数进行了初始化,但是 使用对象初始化程序 收集后,引用将 alive
为什么@is == false@is2 == true
为什么weak2GC.Collect()之后仍有对象,但weak没有?

1 个答案:

答案 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