Search code examples
c#c#-4.0weak-references

Strange C# Weak Reference Behaviour


I'm spending some time learning how weak references work in C# and I came across this odd behaviour.

In the sample code below, the first test passes and the second fails. It seems that you can't modify an instance of an object after construction but before creating a weak reference to it, without stopping the weak reference working in the expected manner.

private class Simple
{
    public Simple() { X = "Hello"; }
    public string X { get; set; }
}

[Test]
public void CreatingWeakReferenceBeforeModifying()
{
    var a = new Simple();
    var aRef = new WeakReference(a);
    a.X = "World";  // First modification to a after creating reference
    a = null;
    GC.Collect();
    Assert.That(aRef.IsAlive, Is.False);  // This assertion passes
}

[Test]
public void CreatingWeakReferenceAfterModifying()
{
    var b = new Simple {X = "World"};  // First mod to b before creating ref
    var bRef = new WeakReference(b);
    b = null;
    GC.Collect();
    Assert.That(bRef.IsAlive, Is.False);  // This assertion fails
}

Have I missed something here?


Solution

  • I suspect that you'd only see this in certain circumstances - it's particularly likely under a debug build, especially under the debugger.

    This statement:

    var b = new Simple {X = "World"};
    

    is effectively:

    var tmp = new Simple();
    tmp.X = "World";
    var b = tmp;
    

    So even though you're setting b to null, there's still a local variable on the stack which has a reference to the object.

    When running in an optimized scenario, I'd expect the GC to notice that the local variable will never be read again, and ignore it as a GC root - but possibly the way you're running it isn't letting the GC be that aggressive.