0

I've got a quick code piece to test weak reference, I expect that after the object being GC, the weak reference should not longer return a object referenct. But my test shows this is not expected:

class Person
{
    private int mI = 3;
    public int MI { get => mI; set => mI = value; }
}
class UseWeakReference
{
    public static void Main(String[] args)
    {
        Person person = new Person();
        WeakReference<Person> wr = new WeakReference<Person>(person);

        wr.TryGetTarget(out Person p1);
        Console.WriteLine(p1);

        person = null;
        wr.TryGetTarget(out Person p2);
        Console.WriteLine(p2);

        p2 = null;
        System.GC.Collect();
        Thread.Sleep(1000);
        wr.TryGetTarget(out Person p3);
        Console.WriteLine(p3); // I expected null here becaure person is collected.
    }
}

It prints:

MyApp1.Person
MyApp1.Person
MyApp1.Person // Why still valid?

Where did I get wrong?

Thanks.

Troskyvs
  • 7,537
  • 7
  • 47
  • 115
  • You didn’t set `p1 = null`, for one. – Ry- Jun 10 '19 at 01:55
  • For a `WeakReference` you should be checking `.Target` property to see if the item has been GC'd. e.g. `MyApp1.Person.Target == null`. See [the docs](https://learn.microsoft.com/en-us/dotnet/api/system.weakreference.target?view=netframework-4.8) for more info – Simply Ged Jun 10 '19 at 02:03

1 Answers1

2

When you call TryGetTarget on a weak reference, assuming that the referenced object has not already been collected, you get back a strong reference to that object. You do this 3 times in your code: p1, p2 and p3 are strong references to the object. When the garbage collector runs - automatically or in your case when you force garbage collection - those strong references will prevent the object being collected.

Here's a version that works:

void Main()
{
    var person = new Person();
    WeakReference<Person> weak = new WeakReference<Person>(person);
    person = null;

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine($"{i}\t{TestReference(weak)}");
        Thread.Sleep(100);
    }

    GC.Collect();
    Console.WriteLine(TestReference(weak));
}

class Person
{
    private int mI = 3;
    public int MI { get => mI; set => mI = value; }
}

bool TestReference(WeakReference<Person> weak)
{
    if (weak.TryGetTarget(out Person p))
    {
        p = null;
        return true;
    }
    return false;
}

Note that at no point are we keeping a strong reference to the object alive for more than a few cycles, and by the time the garbage collector runs there are no strong references to the object so the object is collected.

Even in this code though if I comment out the p = null; line the garbage collector may not collect the object. Try it and see.

The moral of the story is: when you obtain a strong reference from a WeakReference<>, always null the strong reference when you're done with it.

Corey
  • 15,524
  • 2
  • 35
  • 68