Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using an Object Initializer keep an object alive?

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?

like image 252
Jim Avatar asked Aug 21 '13 20:08

Jim


People also ask

What is object initialization why it is required?

In object initializer, you can initialize the value to the fields or properties of a class at the time of creating an object without calling a constructor. In this syntax, you can create an object and then this syntax initializes the freshly created object with its properties, to the variable in the assignment.

What happens when you initialize an object?

Initializing an ObjectThis class contains a single constructor. You can recognize a constructor because its declaration uses the same name as the class and it has no return type. The constructor in the Point class takes two integer arguments, as declared by the code (int a, int b).

What is an object initializer?

An object initializer is an expression that describes the initialization of an Object . Objects consist of properties, which are used to describe an object. The values of object properties can either contain primitive data types or other objects.

Why do we initialize objects in Java?

Initializing an object means storing data into the object. Let's see a simple example where we are going to initialize the object through a reference variable. We can also create multiple objects and store information in it through reference variable.


1 Answers

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.

like image 167
Jon Skeet Avatar answered Sep 19 '22 22:09

Jon Skeet