Does the Garbage Collector benefit from multiple calls to Collect and WaitForPendingFinalizers()?

I found this code online that is appended after deinitializing Excel Interop objects:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

Is this quasi-violation of DRY (calling GC.Collect() and GC.WaitForPendingFinalizers() twice in succession, in stuttering fashion) in any way helpful, or just a waste of time?

Upvotes: 3

Views: 1266

Answers (3)

Artak
Artak

Reputation: 2897

The short answer, is no, you shouldn't need to call it twice.

Now, let's dig a bit deeper. GC uses generation-based approach. That is - the heap is split into chunks, which are used to optimize GC so it actually runs on smaller memory areas. You can learn more about how GC works here.

When GC runs, it reclaims all the objects from the current generation, which to there are no strong* references (* - it's possible to use System.WeakReference type to have a reference to an object, while allowing GC to reclaim it). Those objects, which survive the garbage collection, are moved into the next generation. If the next generation is full enough, GC will run on it too - moving the survived objects further. Having said this, you can see how running the GC the second time will most probably have no difference.

However, in scenarios with heavy load, where objects are created very fast, the time period between two subsequent calls can be enough (depends on the thread's state, priority - which on the calls are made) to create enough objects, which can add some weight to the memory. In that situation the subsequent call can have an effect and clean up a good chunk of memory. While this scenario is potentially possible, it's not really very usual to happen. The reason being that if there is such a load, it will probably continue, so calling GC twice will not have a big difference...

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 942338

Does the Garbage Collector benefit from multiple calls...

You are not doing this to benefit the GC, far from it, you are doing this to force Excel.exe to terminate. It cannot stop until all the interop wrappers (RCWs) are finalized. A single GC.Collect() call is enough to set that train in motion.

The subsequent GC.WaitForPendingFinalizers() call is optional. There are few reasons to want to wait until they are done. In a well-behaved program this happens within a few milliseconds at most. It is only ever important to do this if the thread that owns these interop wrappers is going to terminate. This is not common. If you are not sure then using it is not wrong.

The second Collect+Wait calls are not useful. The RCWs are very small so the Collect call won't free up any useful amount of memory. Wait can't have anything to wait for.

Placement of this code tends to be important. If it appears in the same method where the Excel interfaces are used then it won't do anything when a debugger is attached. Putting it in the caller of the method is best. Why the debugger plays a role is explained in this post.

Upvotes: 7

Matt LaCrosse
Matt LaCrosse

Reputation: 809

This from the example program on the Microsoft documentation site.

   //Force garbage collection.
   GC.Collect();

   // Wait for all finalizers to complete before continuing.
   // Without this call to GC.WaitForPendingFinalizers, 
   // the worker loop below might execute at the same time 
   // as the finalizers.
   // With this call, the worker loop executes only after
   // all finalizers have been called.
   GC.WaitForPendingFinalizers();

When the garbage collector finds objects that can be reclaimed, it checks each object to determine the object's finalization requirements. If an object implements a finalizer and has not disabled finalization by calling SuppressFinalize, the object is placed in a list of objects that are marked as ready for finalization. The garbage collector calls the Finalize methods for the objects in this list and removes the entries from the list. This method blocks until all finalizers have run to completion.

The thread on which finalizers are run is unspecified, so there is no guarantee that this method will terminate. However, this thread can be interrupted by another thread while the WaitForPendingFinalizers method is in progress. For example, you can start another thread that waits for a period of time and then interrupts this thread if this thread is still suspended.

From what I can tell there's no clear indication of a benefit of calling it twice.

Upvotes: 2

Related Questions