GC.Collect() and Finalize
Asked Answered
E

5

46

Ok, it's known that GC implicitly calls Finalize methods on objects when it identifies that object as garbage. But what happens if I do a GC.Collect()? Are the finalizers still executed? Someone asked me this and I answered a "Yes" and then I thought: "Was that fully correct?"

Eckert answered 19/12, 2012 at 14:46 Comment(4)
Why you didn't try this by making log in destructor to see did 'Finalize' called or not? or i miss something?!Uribe
This is certainly not a "stupid question".Nickelson
@Uribe - Yes I could have done that and avoided posting the question. But then the knowledge would just confine to me. The good part is - by posting it here, I got some more elaborate answers on the topic, and I hope it helps other SO users too!Eckert
See also: Are .net finalizers always executed?Clerissa
N
78

Ok, it's known that GC implicitly calls Finalize methods on objects when it identifies that object as garbage.

No no no. That is not known because in order to be knowledge a statement must be true. That statement is false. The garbage collector does not run finalizers as it traces, whether it runs itself or whether you call Collect. The finalizer thread runs finalizers after the tracing collector has found the garbage and that happens asynchronously with respect to a call to Collect. (If it happens at all, which it might not, as another answer points out.) That is, you cannot rely on the finalizer thread executing before control returns from Collect.

Here's an oversimplified sketch of how it works:

  • When a collection happens the garbage collector tracing thread traces the roots -- the objects known to be alive, and every object they refer to, and so on -- to determine the dead objects.
  • "Dead" objects that have pending finalizers are moved onto the finalizer queue. The finalizer queue is a root. Therefore those "dead" objects are actually still alive.
  • The finalizer thread, which is typically a different thread than the GC tracing thread, eventually runs and empties out the finalizer queue. Those objects then become truly dead, and are collected in the next collection on the tracing thread. (Of course, since they just survived the first collection, they might be in a higher generation.)

As I said, that's oversimplified; the exact details of how the finalizer queue works are a bit more complicated than that. But it gets enough of the idea across. The practical upshot here is that you cannot assume that calling Collect also runs finalizers, because it doesn't. Let me repeat that one more time: the tracing portion of the garbage collector does not run finalizers, and Collect only runs the tracing part of the collection mechanism.

Call the aptly named WaitForPendingFinalizers after calling Collect if you want to guarantee that all finalizers have run. That will pause the current thread until the finalizer thread gets around to emptying the queue. And if you want to ensure that those finalized objects have their memory reclaimed then you're going to have to call Collect a second time.

And of course, it goes without saying that you should only be doing this for debugging and testing purposes. Never do this nonsense in production code without a really, really good reason.

Nickelson answered 19/12, 2012 at 15:3 Comment(11)
I blindly believed msdn.microsoft.com/en-us/magazine/bb985010.aspx. The "Finalization" section in the article mentions that "the garbage collector calls the object's Finalize method".... Can't believe such false statements are recorded in the msdn magazine!Eckert
@Sandeep: Well, I suppose it depends on whether you consider the finalizer thread to be part of the garbage collector or not. I think of the garbage collector as specifically the thing that traces the roots, but if you think of the garbage collector as being all the mechanisms that deal with cleanup then I suppose that's true. I'll clarify the answer.Nickelson
So what is it actually? As you said, is GC a "root tracer" or is it a "complete cleaning machine"? I just don't want to think/assume on that. I'm just searching for the truth!Eckert
@Sandeep: The important truth to search for is not what we mean by "the garbage collector" but rather, the answer to your original question: how can we guarantee that finalizers have been run? You guarantee that by waiting for the finalizer thread to do its work.Nickelson
I agree completely to that. But I just wanted to know (bad curiosity)- Who notifies the finalizer thread to start execution? Is it the GC?Eckert
@Sandeep: I think so, but I am not sure; I never worked on the GC itself. This question might be of interest to you: #5223825Nickelson
Nice to see you back on SO, Eric.Jumbled
@MgSam: Thanks! It is nice to have some free time to spend on it! We'll see how long it lasts.Nickelson
Isn't it correct to say that objects from the finalizer queue always end up in a higher generation before they are actually collected?Pericycle
@HenkHolterman: No. Objects that enter the finalization queue after being in the large object heap do not go to a higher generation because the LOH is not generational. Objects that enter the finalization queue after being in the 2-generation do not go to a higher generation because 2 is the highest. Objects that enter the finalization queue on non-generational versions of the CLR, such as the compact framework, do not go to a higher generation.Nickelson
You're right of course, I was thinking about the 'normal' heap only. Where the finalized objects will at least have left gen 0.Pericycle
E
15

Actually the answer "It depends". Actually there is a dedicated thread that executes all finalizers. That means that call to GC.Collect only triggered this process and execution of all finalizers would be called asynchronously.

If you want to wait till all finalizers would be called you can use following trick:

GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();
Eileen answered 19/12, 2012 at 14:56 Comment(1)
@OP: There are reasons why you might want to do this (e.g., as a performance optimization). However, if the motivation to use such code is to fix program logic, the code should probably be using IDisposable.Dispose (ideally via a using statement) instead.Clerissa
A
10

Yes, but not straight away. This excerpt is from Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework (MSDN Magazine) (*)

"When an application creates a new object, the new operator allocates the memory from the heap. If the object's type contains a Finalize method, then a pointer to the object is placed on the finalization queue. The finalization queue is an internal data structure controlled by the garbage collector. Each entry in the queue points to an object that should have its Finalize method called before the object's memory can be reclaimed.

When a GC occurs ... the garbage collector scans the finalization queue looking for pointers to these objects. When a pointer is found, the pointer is removed from the finalization queue and appended to the freachable queue (pronounced "F-reachable"). The freachable queue is another internal data structure controlled by the garbage collector. Each pointer in the freachable queue identifies an object that is ready to have its Finalize method called.

There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method. Because of this, you should not execute any code in a Finalize method that makes any assumption about the thread that's executing the code. For example, avoid accessing thread local storage in the Finalize method."

(*) From November 2000, so things might have changed since.

Ansate answered 19/12, 2012 at 14:55 Comment(0)
U
5

When the garbage is collected (whether in response to memory pressure or GC.Collect()), the objects requiring finalization are put to finalization queue.

Unless you call GC.WaitForPendingFinalizers(), the finalizers may continue to execute in the background long after garbage collection has finished.


BTW, there is no guarantee finalizers will be called at all. From MSDN...

The Finalize method might not run to completion or might not run at all in the following exceptional circumstances:

  • Another finalizer blocks indefinitely (goes into an infinite loop, tries to obtain a lock it can never obtain and so on). Because the runtime attempts to run finalizers to completion, other finalizers might not be called if a finalizer blocks indefinitely.
  • The process terminates without giving the runtime a chance to clean up. In this case, the runtime's first notification of process termination is a DLL_PROCESS_DETACH notification.

The runtime continues to Finalize objects during shutdown only while the number of finalizable objects continues to decrease.

Ubiquitarian answered 19/12, 2012 at 14:57 Comment(0)
T
0

Couple of more points are worth to state here.

Finalizer is the last point where .net objects can release unmanaged resources. Finalizers are to be executed only if you don’t dispose your instances correctly. Ideally, finalizers should never be executed in many cases. Because proper dispose implementation should suppress the finalization.

Here is an example for correct IDispoable Implementation.

If you call the Dispose method of any disposable objects, it should clear all references and Supress the finalization. If there is any not so good developer who forget to call the Dispose method, Finalizer is the life saver.

Twink answered 31/1, 2016 at 22:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.