Hamish
Hamish

Reputation: 619

Why does this weak reference test fail?

Exploring weak references, I wrote the below test to validate my understanding, but the test fails.

As far as I can tell, there are no references to the object that gets "newed up" in the first line (other than the one made by the WeakReference instance), begging the question, why does weakRef think it's Target is still alive?

What am I missing? Why does the test fail and what is still maintaining a reference to the object?

[TestMethod]
public void WeakReference()
{
    var weakRef = new WeakReference(new object(), false);
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Assert.IsFalse(weakRef.IsAlive);
    Assert.IsNull(weakRef.Target);
}

Upvotes: 1

Views: 92

Answers (1)

antiduh
antiduh

Reputation: 12425

I'm able to replicate your results using a normal program (not running it in mstest).

If I run the following in a non-optimized build, it prints: "Alive: true".

    private static void Main()
    {
        WeakReference weakRef = new WeakReference( new object(), false );

        GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced, true, true );
        GC.WaitForPendingFinalizers();

        // Prints true in debug, false in release.
        Console.WriteLine( "Alive: " + weakRef.IsAlive );
    }

If I compile with optimizations enabled, it instead works, printing "Alive: false".

Additionally, if I extract the initialization of the WeakReference to a separate method, it works regardless of the optimization:

    private static void Main()
    {
        WeakReference weakRef = Init();

        GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced, true, true );
        GC.WaitForPendingFinalizers();

        // Always prints false.
        Console.WriteLine( "Alive: " + weakRef.IsAlive );
    }

    private static WeakReference Init()
    {
        return new WeakReference( new object(), false );
    }

To be honest, I'm not sure of the cause. One hypothesis I had was that the temporary object was being stored in a hidden temporary variable on the stack as part of the lack of optimization. However, that doesn't make sense since the GC is able to inspect the stack to determine when the last reference to any variable has been made - the GC can collect things that are in the currently running function, so long as it's not being used anymore.

Upvotes: 3

Related Questions