Could a class instance that is not being assigned to a variable get garbage collected too early?

(I don't even know whether my question makes sense at all; it is just something that I do not understand and is spinning in my head for some time)

Consider having the following class:

public class MyClass
{
    private int _myVar;

    public void DoSomething()
    {
        // ...Do something...

        _myVar = 1;

        System.Console.WriteLine("Inside");
    }
}

And using this class like this:

public class Test
{
    public static void Main()
    {
        // ...Some code...
        System.Console.WriteLine("Before");

        // No assignment to a variable.
        new MyClass().DoSomething();

        // ...Some other code...
        System.Console.WriteLine("After");
    }
}

(Ideone)

Above, I'm creating an instance of a class without assigning it to a variable.

I fear that the garbage collector could delete my instance too early.

My naive understanding of garbage collection is:

"Delete an object as soon as no references point to it."

Since I create my instance without assigning it to a variable, this condition would be true. Obviously the code runs correct, so my asumption seems to be false.

Can someone give me the information I am missing?

To summarize, my question is:

(Why/why not) is it safe to instantiate a class without asigning it to a variable or returning it?

I.e. is

new MyClass().DoSomething();

and

var c = new MyClass();
c.DoSomething();

the same from a garbage collection point-of-view?

Jon Skeet
people
quotationmark

It's somewhat safe. Or rather, it's as safe as if you had a variable which isn't used after the method call anyway.

An object is eligible for garbage collection (which isn't the same as saying it will be garbage collected immediately) when the GC can prove that nothing is going to use any of its data any more.

This can occur even while an instance method is executing if the method isn't going to use any fields from the current execution point onwards. This can be quite surprising, but isn't normally an issue unless you have a finalizer, which is vanishingly rare these days.

When you're using the debugger, the garbage collector is much more conservative about what it will collect, by the way.

Here's a demo of this "early collection" - well, early finalization in this case, as that's easier to demonstrate, but I think it proves the point clearly enough:

using System;
using System.Threading;

class EarlyFinalizationDemo
{
    int x = Environment.TickCount;

    ~EarlyFinalizationDemo()
    {
        Test.Log("Finalizer called");
    }    

    public void SomeMethod()
    {
        Test.Log("Entered SomeMethod");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Thread.Sleep(1000);
        Test.Log("Collected once");
        Test.Log("Value of x: " + x);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Thread.Sleep(1000);
        Test.Log("Exiting SomeMethod");
    }

}

class Test
{
    static void Main()
    {
        var demo = new EarlyFinalizationDemo();
        demo.SomeMethod();
        Test.Log("SomeMethod finished");
        Thread.Sleep(1000);
        Test.Log("Main finished");
    }

    public static void Log(string message)
    {
        // Ensure all log entries are spaced out
        lock (typeof(Test))
        {
            Console.WriteLine("{0:HH:mm:ss.FFF}: {1}",
                              DateTime.Now, message);
            Thread.Sleep(50);
        }
    }
}

Output:

10:09:24.457: Entered SomeMethod
10:09:25.511: Collected once
10:09:25.562: Value of x: 73479281
10:09:25.616: Finalizer called
10:09:26.666: Exiting SomeMethod
10:09:26.717: SomeMethod finished
10:09:27.769: Main finished

Note how the object is finalized after the value of x has been printed (as we need the object in order to retrieve x) but before SomeMethod completes.

people

See more on this question at Stackoverflow