Why can't the GC understand this?

My question is simple, why can't the GC understand that the timer object in main should be garbage collected together with the timer inside TestTimer and related EventHandler ?

Why am I constantly getting console.Writeline output?

 class Program { public static void Main() { TestTimer timer = new TestTimer(); timer = null; GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadKey(); } } public class TestTimer { private Timer timer; public TestTimer() { timer = new Timer(1000); timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); timer.Start(); } private void timer_Elapsed(Object sender, ElapsedEventArgs args) { Console.Write("\n" + DateTime.Now); } } 
+6
source share
5 answers

Not to depend on the GC, use the Dispose pattern to correctly place the TestTimer (which Timer should then have).

However, what happens is that the timer saves its life by receiving the GC handle on its own. Read this blog post:

http://nitoprograms.blogspot.com/2011/07/systemthreadingtimer-constructor-and.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FOlZtT+%28Nito+Programming%29

+7
source

You do not have a timer after use. This is what delays its collection.

If your class contains objects that implement IDisposable (e.g. Timer ), it must also implement IDisposable .

 public class TestTimer : IDisposable { private Timer timer; public TestTimer() { timer = new Timer(1000); ... } #region IDisposable public void Dispose() { Dispose(true); } volatile bool disposed = false; protected virtual void Dispose(bool disposing) { if (disposing && !disposed) { timer.Dispose(); GC.SupressFinalize(this); disposed = true; } } ~TestTimer() { Dispose(false); } #endregion } 

Your main method should look like this:

 public static void Main() { using (TestTimer timer = new TestTimer()) { // do something } GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadKey(); } 

Again, if your TestTimer needs to live longer than the scope of one Main method, then the class that creates it and contains the link must also implement IDisposable .

+4
source

Why do you expect an active timer to be built first? My expectation is that it acts as the root of the GC. Otherwise, the timer will stop working because you no longer have the link.

+4
source

When you start the timer timer.Start() , a new thread (s) will start in the background. When you call timer = null; , you do not stop the thread (s) used by the timer. The garbage collector will not kill or interrupt threads that run independently of the source object that creates these threads.

+2
source

It turns out that this state parameter (and the TimerCallback delegate) has an interesting effect on garbage collection: if none of them refer to the System.Threading.Timer object, it can be garbage collection, causing it to stop. This is because both the TimerCallback delegate and the state parameter are wrapped in GCHandle. If none of them refers to a timer object, it may be eligible for the GC, freeing the GCHandle from its finalizer.

See this for more details.

+1
source

Source: https://habr.com/ru/post/893405/


All Articles