My .NET Core 2.1 application, running on Windows x64, allocates large arrays of structs, each struct holding a class in a field. The arrays end up on the Large Object Heap, so they are not collected until a full (gen 2) GC. That was expected. What surprised me a bit is that the classes referenced by the structs also don't seem to get GCed until a gen 2 GC. Is this expected behaviour?

Something like this happens if I have a large HashSet as well, because internally a HashSet keeps an array of HashSet.Slot - a struct.

Some code that reproduces the problem:

    class MyClass
    {
        public long Value;

        ~MyClass()
        {
            if (Value < 10)
            {
                Console.WriteLine("MyClass finalizer " + Value);
            }
        }
    }

    struct TheEntry
    {
        public MyClass TheClass;
    }

    class Program
    {
        static void Main(string[] args)
        {
            var wr = AllocateSomeStuff();
            Console.WriteLine("Before GC MyClass is in gen " + GC.GetGeneration(wr));

            GC.Collect(1);
            Console.WriteLine("GC done");
            GC.WaitForPendingFinalizers();
            Console.WriteLine("Finalizers done");
            Console.WriteLine("After GC MyClass is in gen " + GC.GetGeneration(wr));
        }

        private static WeakReference<MyClass> AllocateSomeStuff()
        {
            var array = new TheEntry[11000];

            for (int i = 0; i < array.Length; ++i)
            {
                array[i].TheClass = new MyClass { Value = i };
            }

            return new WeakReference<MyClass>(array[0].TheClass);
        }
    }

When I run this with an array of 11,000 elements on a 64-bit system (so it's over the 85KB threshold for LOH) the finalizers for MyClass do not run. Output:

Before GC MyClass is in gen 0
GC done
Finalizers done
After GC MyClass is in gen 1

With 10,000 elements they run. I suppose I expected the runtime to GC MyClass as it's unreachable, even though it's still referenced by another unreachable object. Is this how it's supposed to work?

0

There are 0 best solutions below