GC and destructor

Source: Internet
Author: User

GC and destructor

This interesting question thanks to the friendship in assembling the head.

See the following code:

    public class Dummy    {        public static Dummy Instance;        public int X = 1;        ~Dummy()        {            Instance = this;        }    }

Use the following code for calling (I will slightly adjust the output log ):

Task.Run(() =>{    var d = new Dummy();    d = null;    GC.Collect();    GC.WaitForFullGCComplete();}).Wait();var isNull = Dummy.Instance == null;Console.WriteLine(isNull);if (false == isNull){    Console.WriteLine(Dummy.Instance.X);}else{    Console.WriteLine("Oh no!Dummy.Instance is null.");}

Problem: Is the output Instance = null True or False?

Here, you can stop reading the following analysis and think about what your answer will be?

First of all, this question is the kind of feeling that you may have to get in first, even if you know that there is a pitfall. Especially when tasks, GC, static fields, instance fields, and destructor are mixed together with so many things, it is related to multithreading at first glance, which is quite confusing, right?

When I first saw it, I thought that the Task was running for GC collection and then Wait waited until the Task was finished. The variable d pointed to the object because of GC. waitForFullGCComplete () should have been successfully reclaimed by garbage collection. When you execute the destructor, the current object that the static variable Instance points to (that is, the reference object that the variable d points to at the beginning) should be null, then Instance = null will certainly return True. Or the output should always be a definite value.

However, the actual running effect is not always the same. Please note that, after many experiments, the number of times that True and False are output is uncertain (greater than or equal to 1 and less than or equal to 50000, however, the probability of True appears more than False. The total number of False appears to be between 1 and 10.

To prevent some optimizations of the C # compiler, compare the running effects of Release and Debug respectively, and the results are the same.

Then I cannot figure out why there are two output results. The following code is tested cyclically without Task interference, but the result is similar to that of running tasks. Both outputs are True or False. That is to say, different output is provided when GC code is executed in sequence without tasks.

var d = new Dummy();d = null;GC.Collect();GC.WaitForFullGCComplete();var isNull = Dummy.Instance == null;Console.WriteLine(isNull);if (false == isNull){    Console.WriteLine(Dummy.Instance.X);}else{    Console.WriteLine("Oh no!Dummy.Instance is null.");}

Recently, I just re-learned GC. Recently, I just summarized the GC knowledge and remembered that there was a "extended" Garbage object lifecycle at the end of the destructor, but I couldn't help it. I also wondered whether the Destructor performed special optimization on static fields. For example, after the Instance value is assigned, the GC collection policy is automatically adjusted and the G0 generation is adjusted to the G1 generation, or when the Destructor is executed, this is not automatically recycled, that is, the static field value is assigned with thread-safe control. As a result, this is assigned to the Instance first, and this is set to null only when the Instance is recycled, however, because Instance is a static field and the root of GC, why? I learned a lot of theories and found that practices are still not the case.

I couldn't figure out the root cause. I asked my head, And he gave a brief answer:"The actual race condition is the thread that the Finalizer executes ..".

Destructor competing condition, Finalizer, thread? Oh, wait, etc. main thread, thread pool hosting thread for the current Task, GC thread, Finalizer thread, are there any competition between several threads (such as the GC thread and the Finalizer thread) or between threads of the same type (such as the Finalizer thread and the Finalizer thread) that produce the race condition?

Following this idea, I can print out the thread ID and compare it. Isn't there any conclusion?

Severe statement: I do not know how to execute the Destructor here ~ Whether the current thread is the Finalizer thread in Dummy (). reading a book is like this, but no code is provided. In this article, we will temporarily name this thread as the Finalizer thread. If you know how to correctly obtain the GC thread and Finalizer thread, please do not give us any further advice.

Immediately, I adjusted the code and printed some more logs. Although the printed logs are a bit messy, I am sure the ID of the managed thread for Task execution and destructor execution is different,The thread IDs of managed threads in the Destructor are always the same..

Public class Dummy {public static Dummy Instance; public int X = 1; public static ConcurrentBag <int> threadIDBag = new ConcurrentBag <int> ();~ Dummy () {var threadId = Thread. currentThread. managedThreadId; Console. writeLine ("Destructor CurrentContext ThredID: {0}", threadId); if (threadIDBag. contains (threadId) = false) {threadIDBag. add (threadId);} Instance = this; // Console. writeLine ("Destructor === Instance is null: {0}", Instance = null );}}Dummy

The call code is as follows:

Static void Main (string [] args) {var counter = 0; // statistics Dummy Instance is not null count var testCnt = 1; // 50000; // number of tasks executed while (testCnt> 0) {testCnt --; task. run () => {var d = new Dummy (); d = null; GC. collect (); GC. waitForFullGCComplete (); Console. writeLine ("Task CurrentContext ThredID: {0}", Thread. currentThread. managedThreadId );}). wait (); var isNull = Dummy. instance = null; Conso Le. writeLine (isNull); if (false = isNull) {Console. writeLine (Dummy. instance. x); counter ++;} else {Console. writeLine ("Oh no! Dummy Instance is null. ");} Console. writeLine ("==================");} Thread. sleep (1, 2000); Console. writeLine ("End Task ...... "); Console. writeLine ("Dummy Instance is not null counter: {0}", counter); Console. writeLine ("Finalizer ThreadID Count: {0}", Dummy. threadIDBag. count); // The output here is 1 Console. readKey ();}RunTask

Here I am sure that the "Race Condition" mentioned in the Assembly head is definitely not the race State between the Finalizer thread and the Finalizer thread, or the race State between the GC thread and the Finalizer thread.

Because the head says that after a Task is run, Wait should not be the competition between the managed thread and the Finalizer thread allocated by the Task operation.

Therefore, thread competition occurs between the execution call thread (in this example, the main thread that calls Console. WriteLine () after the Task is executed) and the Finalizer thread.

The conclusion that can be drawn here is that the application execution thread MainThread runs the code Console. writeLine (Dummy. when Instance = null), The Destructor thread FinalizerThread may have just been executed but has not run the Instance = this line of code, so Dummy. the Instance is not empty, and the output is False.

Simply put, the output is different because of the execution uncertainty of the Finalizer thread.

Do you think this is true?

Add three questions:

1. What if I change GC. WaitForFullGCComplete () to GC. WaitForPendingFinalizers?

2. For example, if Dummy inherits from IDisposable, what is the thread ID that executes the Dispose () method?

3. How to directly and correctly obtain the GC thread and Finalizer thread? Are all managed threads in the thread pool?

Learn more, think more, and work harder, and learn the truth.

 

Refer:

<CLR Via C #>

Http://www.cppblog.com/Solstice/archive/2010/01/28/dtor_meets_threads.html

Http://msdn.microsoft.com/zh-cn/library/system.idisposable.dispose%28v=vs.110%29.aspx

Http://blogs.msdn.com/ B /dotnet/archive/2014/11/12/net-core-is-open-source.aspx

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.