Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# GC.Collect not destroy an object if it's constructed using instance constructor initializer [duplicate]

Possible Duplicate:
Resurrection difference in using Object Initializer

I am having a hard time trying to understand how garbage collector works in C# (I'm using 2012, so c# 4.5). Here is my example code:

    public class A
    {
        public int c;
        public A(){}
        public A(int pC)
        {
            c = pC;
        }
    }

    public static void Main()
    {
        // Test 1
        var a = new A {c=199};
        var aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
        //            Console.WriteLine(GC.GetGeneration(aRef.Target)); //output 1

        // Test 2
        a = new A (200);
        aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
    }

Output is True / True / True / False

It seems to me in both tests, the object on the heap has no root before calling GC.Collect. But it happens that in Test 1, the object get through the force gc run, while in Test 2 it doesn't. So, is there something mysterious going on about using initializer? My guess is that there might be "some extra code" when use initializer that would become a strong root for the same object.....

Thanks.

like image 601
alex1234231 Avatar asked Jan 24 '13 04:01

alex1234231


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

What is C language basics?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.


2 Answers

When using the initializer say

 var a = new A {c=199}; --------> 1

compiler includes an extra reference on stack which makes object get through GC .
The statement (1) in the above turns out to be as follows

 var temp = new A() ;
  temp.c=199;
  var a=temp . 

I think this temp variable makes this object alive during GC .

Please refer this link

EDIT : As mentioned by TomTom in comments. If debugger is running then GC behaviour will be changed and variabes are kept alive until end of method, not until last use . Please correct me if I,m wrong

like image 92
Imposter Avatar answered Oct 04 '22 20:10

Imposter


Clearly you are running either the Debug build or have a debugger attached. The garbage collector gets lifetime hints from the just-in-time compiler, it generates a table that indicates in what sections of code a local variable can be referenced. The garbage collector walks the stack of the executing method that was interrupted by the GC and checks the execution location against this table. And counts the reference as valid when it finds a match.

If the code was built in the Debug configuration or when a debugger is attached, the jitter modifies this table and lets the variable stay alive until the end of the method body. This makes it a lot easier to debug the code, you can put the local variable in a watch expression and it will produce a result even when you step past the point where the variable is no longer used.

The answer posted by @Imposter is correct, the hidden temporary variable keeps the first instance of A alive. And the garbage collector considers it valid until the end of the method because you are using the debugger. Your second a = null; assignment allows the second instance to be garbage collected.

What will really happen when you run this code in production is very different. For one, the jitter optimizer will remove the a = null assignments. It knows that those assignments have no useful side-effects so it generates no code for them. Pretty unintuitive, the best way to see this is by taking these steps:

  • Delete the a = null assignments from the code
  • Switch to the Release configuration with Build + Configuration Manager
  • Use Tools + Options, Debugging, General, untick the "Suppress JIT optimization on module load" option.

The last option change allows you to keep using the debugger without it affecting the way the jitter generates code. Now the temporary variable will no longer keep the first instance of A referenced, the table generated by the jitter will only mark it as storing a valid reference for the first statement in the method. Run your program and you'll see:

True
False
True
False

With the important new insight that setting a reference to null is not actually necessary, the garbage collector is smart enough to not require you to help.

like image 20
Hans Passant Avatar answered Oct 04 '22 21:10

Hans Passant