Why does using an Object Initializer keep an object alive?

378 views Asked by At

I recently came across this SO article and tweaked it for my scenario which follows:

using System;
using System.Collections.Generic;

namespace ConsoleApplication18
{
    class Program
    {
        static void Main(string[] args)
        {
            Manager mgr = new Manager();
            var obj = new byte[1024];

            var refContainer = new RefContainer();
            refContainer.Target = obj;

            obj = null;

            mgr["abc"] = refContainer.Target;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // true (still ref'd by "obj")

            refContainer = null;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // false (no remaining refs)           
        }
    }

    class RefContainer
    {
        public object Target { get; set; }
    }

    class Manager
    {
        Dictionary<string, WeakReference> refs =
        new Dictionary<string, WeakReference>();
        public object this[string key]
        {
            get
            {
                WeakReference wr;
                if (refs.TryGetValue(key, out wr))
                {
                    if (wr.IsAlive)
                        return wr.Target;
                    refs.Remove(key);
                }
                return null;
            }
            set
            {
                refs[key] = new WeakReference(value);
            }
        }
    }
}

Running this program gives the following expected result:

True
False
Press any key to continue . . .

However change this:

var refContainer = new RefContainer();
refContainer.Target = obj;

To this (using Object Initializer syntax):

var refContainer = new RefContainer() { Target = obj };

Gives the following output:

True
True
Press any key to continue . . .

What's going on here? Why would lifetime of the reference be different just because of using Object Initializer?

1

There are 1 answers

6
Jon Skeet On BEST ANSWER

Why would lifetime of the reference be different just because of using Object Initializer?

I can't actually reproduce your problem anyway, but I suspect it's because this:

var refContainer = new RefContainer() { Target = obj };

is equivalent to:

var tmp = new RefContainer();
tmp.Target = obj;
var refContainer = tmp;

... so you end up with an extra reference to the object on the stack. Now when running not under the debugger, I'd expect the GC to notice that that stack location is never read again, and allow the object to be garbage collected - but as you're running under the debugger, the GC is more conservative, and I suspect it treats all stack variables as GC roots.

That's just a guess though - without being able to reproduce it anyway, it's hard to say for sure.

EDIT: Your assignments of obj = null; and refContainer = null; are pointless under non-debug mode; because the variables aren't read after that point anyway, the GC ignores them as GC roots.