Search code examples
c#.netgarbage-collection

Why isn't the object collected by GC in C# when assigned null and GC.Collect() is called, despite a background task referencing it?


In the code below, the Starter object is set to null, and GC.Collect() is called, but the background task still runs and the object isn't collected:


namespace ConsoleApp5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Starter s = new();
            s.Start();
            s = null;
            GC.Collect(0, GCCollectionMode.Forced);
            GC.Collect(1, GCCollectionMode.Forced);
            GC.Collect(2, GCCollectionMode.Forced);
            Console.ReadLine();
        }
    }
    public class Starter
    {
        int a = 0;
        public void Start()
        {
            Task.Run(() =>
            {
              
                while (true)
                {
                    a++;
                    Console.WriteLine(a);
                }
            });
        }
    }
}



Solution

  • Thread invocation methods (and thus their targets) count as a GC root, so the moment Start() is invoked, the object is rooted by the thread machinery. That thread is short-lived and only serves to create a thread-pool item - and almost certainly hasn't even started by the time GC.Collect() executes (and completes), but even if it had finished: the thread-pool queue and active workers are also rooted (via the thread-pool machinery). Since a is a field on the object, and the thread-pool callback touches that field: the thread-pool task can see the object, thus it is not collectable.

    However, it is very likely that at the time GC.Collect() executes, the ThreadStart delegate is still in a pending queue waiting for the thread to be fully created and activated - but since the ThreadStart has a target of the Starter object you created: it is still reachable and thus not collectable.