Gc.Collect()

When is it acceptable to call GC.Collect?

If you have good reason to believe that a significant set of objects - particularly those you suspect to be in generations 1 and 2 - are now eligible for garbage collection, and that now would be an appropriate time to collect in terms of the small performance hit.

A good example of this is if you've just closed a large form. You know that all the UI controls can now be garbage collected, and a very short pause as the form is closed probably won't be noticeable to the user.

UPDATE 2.7.2018

As of .NET 4.5 - there is GCLatencyMode.LowLatency and GCLatencyMode.SustainedLowLatency. When entering and leaving either of these modes, it is recommended that you force a full GC with GC.Collect(2, GCCollectionMode.Forced).

As of .NET 4.6 - there is the GC.TryStartNoGCRegion method (used to set the read-only value GCLatencyMode.NoGCRegion). This can itself, perform a full blocking garbage collection in an attempt to free enough memory, but given we are disallowing GC for a period, I would argue it is also a good idea to perform full GC before and after.

Source: Microsoft engineer Ben Watson's: Writing High-Performance .NET Code, 2nd Ed. 2018.

See:

  • https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode(v=vs.110).aspx
  • https://msdn.microsoft.com/en-us/library/dn906204(v=vs.110).aspx

The purpose of GC.Collect()

The garbage collector is not allowed to collect objects that are still referenced. Since you're keeping a reference to refToMyCar to get its generation later, it cannot be collected. If you want to observe the object being reclaimed, you can use WeakReference instead. Also, you need to run without the debugger - to help with debugging, the debugger makes it so that all references survive until they get out of scope (i.e. the block/method body ends).

In your second case, you're allocating a large object. .NET will place those on a special heap, the large object heap. Those objects have special rules - they're always considered generation two, and they cannot be moved (unless you explicitly ask the GC to do that). You need to pay special attention to large objects.

Of course, both of these behaviours are described in the documentation.

The basic rule dealing with GC.Collect is pretty simple - don't use it. There's very few cases where there's any benefit to doing so, and most of the time, you're just wasting CPU, memory and making objects survive longer than they otherwise would.

What's so wrong about using GC.Collect()?

From Rico's Blog...

Rule #1

Don't.

This is really the most important
rule. It's fair to say that most
usages of GC.Collect() are a bad idea
and I went into that in some detail in
the orginal posting so I won't repeat
all that here. So let's move on to...

Rule #2

Consider calling GC.Collect() if some
non-recurring event has just happened
and this event is highly likely to
have caused a lot of old objects to
die.

A classic example of this is if you're
writing a client application and you
display a very large and complicated
form that has a lot of data associated
with it. Your user has just
interacted with this form potentially
creating some large objects... things
like XML documents, or a large DataSet
or two. When the form closes these
objects are dead and so GC.Collect()
will reclaim the memory associated
with them...

So it sounds like this situation may fall under Rule #2, you know that there's a moment in time where a lot of old objects have died, and it's non-recurring. However, don't forget Rico's parting words.

Rule #1 should trump Rule #2 without
strong evidence.

Measure, measure, measure.

What exactly does the gc.collect() function do?

It depends! If called with no argument or with generation=2 as an argument, it would free the objects that are collectable. If called with generation=1 it would not clear the free lists.

From the documentation:

With no arguments, run a full collection. The optional argument generation may be an integer specifying which generation to collect (from 0 to 2). A ValueError is raised if the generation number is invalid. The number of unreachable objects found is returned.

The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) is run. Not all items in some free lists may be freed due to the particular implementation, in particular float.

Does garbage collection run immediately after GC.Collect()?

SHORT ANSWER

Calling GC.Collect() will do a complete garbage collection and wait for it to finish, but it will NOT wait for any pending finalizers to run.

LONG ANSWER

You're partially right in your suppositions because the GC for running finalizers runs in one or more background threads. (But see the footnote at the end of this answer.)

However, it is possible to wait for a complete GC to finish by calling GC.WaitForFullGCComplete() and GC.WaitForPendingFinalizers() after you have called GC.Collect():

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

However, be aware that The thread on which finalizers are run is unspecified, so there is no guarantee that this method will terminate.

Note that you should not normally use the GC in this way; I assume you have a special case that you need to address, or you are doing this for research purposes.

The only valid case I've seen for this is when an application is closing, and you want to (try to) ensure that all finalizers have been run - because, for example, they will flush log files etc.

As noted above, this still doesn't guarantee that all finalizers have been run; it's just the best you can do.

In answer to your point (5):

The documentation for GC.Collect() states:

Forces an immediate garbage collection of all generations.

So this will force a GC.

The documentation also states:

Use this method to try to reclaim all memory that is inaccessible.

The use of the word "try" there merely means that even if a full GC is run, not all inaccessible memory will necessarily be reclaimed. There are several reasons that can occur, for example, a finalizer may block.

Footnote

.Net 4.5 allows you to specify whether GC.Collect() is blocking or not.

In fact, the documentation for GC.Collect() states that It performs a blocking garbage collection of all generations, which would appear to contradict my statements above. However, there seems to be some confusion as to whether this really is the case.

See for example this thread.

The answer is this: GC.Collect() will by default wait for all generations to be GCed, but it will NOT wait for pending finalizers, which are always executed in a separate thread.

Hence if you do not need to wait for finalizers, you ONLY need to call GC.Collect() and you do NOT need to wait for anything else.

Should I call GC.Collect() in Dispose() method?

The dispose never called Garbage Collector automatically, the dispose design to free un-managed resource. The Garbage Collector execution is scheduled processes which will run in after specified time.

When you call .Net Garbage Collector, it calls the Object.Finalize method of an object on garbage collection to free manage resource, that's why your memory use count show less number



Related Topics



Leave a reply



Submit