Garbage collection

Do I need to protect Thread objects from the garbage collector? What about an object that contains a function executed by a thread?

Consider this simple server:

 class Server{ readonly TcpClient client; public Server(TcpClient client){this.client = client;} public void Serve(){ var stream = client.GetStream(); var writer = new StreamWriter(stream); var reader = new StreamReader(stream); writer.AutoFlush = true; writer.WriteLine("Hello"); while(true){ var input = reader.ReadLine(); if(input.Trim() == "Goodbye") break; writer.WriteLine(input.ToUpper()); } client.Close(); } } static void Main(string[] args){ var listener = new TcpListener(IPAddress.Any, int.Parse(args[0])); listener.Start(); while(true){ var client = listener.AcceptTcpClient(); var server = new Server(client); var thread = new Thread(server.Serve); thread.Start(); } } 

Do I have to wrap stream objects in some kind of static collection so that they are not noticed by the garbage collector?

Presumably, if the Thread object itself remains alive, then the Server object will live, because the thread contains a link to a delegate that contains a link to the target. Or perhaps the thread object itself is assembled, but the actual thread continues to work. The Server object is now suitable for the collection. And what happens if he tries to access his fields?

Garbage collection makes my head spin several times. I am glad that I usually do not need to think about it.

Given the potential opportunities here, I would like to believe that the garbage collector is smart enough not to collect thread objects when the thread itself is still running, but I can’t find the documentation saying this. The reflector doesn't help much here, since most of the Thread class is, unsurprisingly, implemented in the MethodImplOptions.InternalCall functions. And I would prefer not to miss my legacy copy of SSCLI for answers (and because it is a pain, but because it is not the right answer).

+7
source share
4 answers

It is pretty simple. The actual thread of execution is not a Thread object. The program runs on real Windows threads that stay alive no matter what your .NET garbage collector does with your Thread objects. So it is safe for you; you don’t need to care about Thread objects if you just want the program to continue to work.

Also note that your threads are not collected when they are started, because they actually belong to the roots application. (The roots are how the garbage collector knows that he is alive.)

More: An accessible Thread object is accessible through Thread.CurrentThread is something like a global static variable, and they are not collected. As I wrote earlier: any managed thread that was launched and now executes some code (even outside of .NET) does not lose the Thread object, because it is firmly connected with the "roots".

+6
source

While your thread is running, it will not be compiled.

+2
source

The startup parameter (which is server.Serve in your case) in the thread designer is a delegate that you already knew.

Presumably, if the Thread object itself remains alive, then the Server object will live, because the thread refers to the delegate which contains a reference to the target object

This is what C # in depth from Jon Skeet has to say about delegate’s goal life

It is worth knowing that a delegate instance will prevent garbage collection if the delegate instance itself cannot be collected.

So yes, server will not be built while var thread is in scope. If var thread goes out of scope before thread.start is called (and there are no other links), then yes, this can be compiled.

Now the big question. Thread.Start () is called and the thread goes out of scope until server.Serve completes, whether the GC can collect the thread.

Instead of digging, let's just test it

 class Program { static void Main(string[] args) { test(); GC.Collect(2); GC.WaitForPendingFinalizers(); Console.WriteLine("test is over"); } static void test() { var thread = new Thread(() => { long i = 0; while (true) { i++; Console.WriteLine("test {0} {1} {2} ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId i); Thread.Sleep(1000); //this is a demo so its okay } }); thread.Name = "MyThread"; thread.Start(); } } 

This is the result. (after adding threadID)

 test MyThread 3 1 test is over test MyThread 3 2 test MyThread 3 3 test MyThread 3 4 test MyThread 3 5 

Therefore, I call a method that creates a thread and starts it. The method ends so that var thread out of scope. But even though I called GC, and the thread variable is out of scope, the thread continues to work.

So, as long as you start the thread before it goes out of scope, everything will work as expected

Update To clarify GC.Collect

It makes it immediately collect garbage of all generations.

Everything that can be collected will be. This is not so, as the comments say “a little more than an expression of intent”. (If he had no reason to caution against calling him). However, I added the argument "2" to make sure that it is gen0 through gen2.

Your assessment of the finalizers is worth noting. So I added GC.WaitForPendingFinalizers , which

... pauses the current thread until the thread that processes the finalizer queue empties the queue.

This means that all finalizers that need to be processed are indeed processed.

The sampling point was that as long as you start the thread, it will work until it is interrupted or terminated, and that the GC will not somehow interrupt the thread just because var thread exited scope

As a third-party, Al Kepp is correct, that indeed, as soon as you start the thread, System.Threading.Thread is rooted. You can find out by using the SOS extension.

eg.

 !do 0113bf40 Name: System.Threading.Thread MethodTable: 79b9ffcc EEClass: 798d8ed8 Size: 48(0x30) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 79b88a28 4000720 4 ....Contexts.Context 0 instance aaef947d00000000 m_Context 79b9b468 4000721 8 ....ExecutionContext 0 instance aaef947d00000000 m_ExecutionContext 79b9f9ac 4000722 c System.String 0 instance 0113bf00 m_Name 79b9fe80 4000723 10 System.Delegate 0 instance 0113bf84 m_Delegate 79ba63a4 4000724 14 ...ation.CultureInfo 0 instance aaef947d00000000 m_CurrentCulture 79ba63a4 4000725 18 ...ation.CultureInfo 0 instance aaef947d00000000 m_CurrentUICulture 79b9f5e8 4000726 1c System.Object 0 instance aaef947d00000000 m_ThreadStartArg 79b9aa2c 4000727 20 System.IntPtr 1 instance 001D9238 DONT_USE_InternalThread 79ba2978 4000728 24 System.Int32 1 instance 2 m_Priority 79ba2978 4000729 28 System.Int32 1 instance 3 m_ManagedThreadId 79b8b71c 400072a 18c ...LocalDataStoreMgr 0 shared static s_LocalDataStoreMgr >> Domain:Value 0017fd80:NotInit << 79b8e2d8 400072b c ...alDataStoreHolder 0 shared TLstatic s_LocalDataStore 

This is a thread called MyThread.

 !do -nofields 0113bf00 Name: System.String MethodTable: 79b9f9ac EEClass: 798d8bb0 Size: 30(0x1e) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll String: MyThread 

Why yes, it is.

What is he attached to?

 !GCRoot 0113bf40 Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info. Scan Thread 7708 OSTHread 1e1c Scan Thread 4572 OSTHread 11dc Scan Thread 9876 OSTHread 2694 ESP:101f7ec:Root: 0113bf40(System.Threading.Thread) ESP:101f7f4:Root: 0113bf40(System.Threading.Thread) DOMAIN(0017FD80):HANDLE(Strong):9b11cc:Root: 0113bf40(System.Threading.Thread) 

As "Production Debugging for .NET Framework Applications" says this is the root.

Note that! GCRoot was launched after its launch. Before it was launched, it did not have HANDLE (Strong).

If the output is HANDLE (Strong), a strong link has been found. This means that the object is rooted and garbage cannot be collected. Other reference types can be found in the Appendix.

For more information on how managed threads are mapped in OS threads (and how you can confirm this with SOS extensions), see this article by Yun Jin,

+1
source

Despite my warning to Conrad Frix about the difficulty of validating things with test code when it comes to multithreading or GC, not to mention both, I put together a simple test. I think the results put a satisfactory rest on this question.

The more I read the answers posted here, the more I thought about it, the more it seemed to come down to one specific question:

How much chaos the finalizer of the Thread object will complete when and whether it will execute before the exit of my thread.

Obviously, the GC itself will not (directly) interrupt the actual thread of execution. And while the Server object Serve() method was running, its this pointer should be protected as an object that is available locally for the current method.

But if the GC assembled and completed the Thread object, what would happen? Since a thread object must represent a thread of execution, will the finalizer complete its thread of execution, bringing them to equilibrium? Or, even worse, will it destroy some kind of internal state that the runtime uses to control the flow?

I really did not want to open Rotor to answer these questions, because it is stupid and because I did not want to rely on the implementation of SSCLI; not for that.

But then I began to wonder if I had thought about this a few years ago. I thought of the Thread object as controlling the flow of execution, implying that the latter would live and die with the former.

But actually, the Thread class is just an interface for the thread of execution - a convenient abstraction. Instead, he must live and die through the flow of execution, and not vice versa.

Looking at this from this point of view, I thought that the structure should already do the job of preserving abstraction, right? Of course! I used it a million times. This is a Thread.CurrentThread property!

The simplest of the tests showed that the created Thread object was indeed accessible from the framework itself:

 Thread t; t = new Thread(o=>Console.WriteLine(o == Thread.CurrentThread)); t.Start(t); 

This tiny test confirmed that regardless of whether I supports a reference to my Thread objects, this makes the framework. And with what is known, everything else is given. The Server object lives on and the thread continues to execute.

Hooray! I can come back so as not to think about the garbage collector for a while!

+1
source

All Articles