Search code examples
c#visual-studioweak-references

WeakReference not Garbage Collected


It is my understanding that after all strong references to the Target of a WeakReference are set to null and the GC is invoked, that weak reference should no longer be alive.

However, the code below does not appear to follow this expectation:

    static void Main(string[] _) {
        var person = new Person();
        var wr = new WeakReference(person);

        person = null;

        // We should have access to the person here
        if (wr.Target is Person shouldExist)
        {
            Console.WriteLine($"Person exists! IsAlive: {wr.IsAlive}.");
            shouldExist = null;
        }
        else
        {
            Console.WriteLine($"Person does not exist :( IsAlive: {wr.IsAlive}.");
        }

        // Invoke GC.Collect.            
        GC.Collect();


        if (wr.Target is Person shouldNotExist)
        {
            Console.WriteLine("This person should have been garbage collected");
            Console.WriteLine($"IsAlive: {wr.IsAlive}");
        }
        else
        {
            Console.WriteLine("This person was garbage collected and weak reference is no longer alive");
            Console.WriteLine($"IsAlive: {wr.IsAlive}");
        }
    }

where

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

And the output is

Person exists! IsAlive: True. This person should have been garbage collected IsAlive: True

I was expecting the output to be:

Person exists! IsAlive: True. This person was garbage collected and weak reference is no longer alive IsAlive: False

Am I missing something here about how weak references work?


Solution

  • The use of the references to the Person object in Main() is keeping the object alive until the end of the method.

    If you change the code as follows (so that the access to the Person object is in a separate method), it will work as expected in C# 12/.NET 8:

    public static class Program                                    
    {
        static void Main()
        {
            var wr = getWeakReference();
            checkPersonExists(wr);
            GC.Collect();
    
            if (wr.Target is Person shouldNotExist)
            {
                Console.WriteLine("This person should have been garbage collected");
                Console.WriteLine($"IsAlive: {wr.IsAlive}");
            }
            else
            {
                Console.WriteLine("This person was garbage collected and weak reference is no longer alive");
                Console.WriteLine($"IsAlive: {wr.IsAlive}");
            }
        }
    
        static WeakReference getWeakReference()
        {
            var person = new Person();
            var wr     = new WeakReference(person);
    
            return wr;
        }
    
        static void checkPersonExists(WeakReference wr)
        {
            // We should have access to the person here
            if (wr.Target is Person)
            {
                Console.WriteLine($"Person exists! IsAlive: {wr.IsAlive}.");
            }
            else
            {
                Console.WriteLine($"Person does not exist :( IsAlive: {wr.IsAlive}.");
            }
    
        }
    }
    
    class Person
    {
        private int mI = 3;
        public  int MI { get => mI; set => mI = value; }
    }
    

    NOTE: This behaviour is not guaranteed and may vary in different versions of C#/.NET.