During my talk on Garbage Collection in .NET at the Jacksonville Code Camp 2007, Joe Healy mentioned that I should take a look at the changes made to the GC in the .NET Framework 3.5 release. (This is based on the Beta 2 release, but it should be pretty stable at this point.)
After doing some research using the SSCLI 2.0 code base to look at the GC class as it exists in .NET 2.0 and Reflector to look at it in .NET 3.5, I found the single change that was made. (For those of you wondering why I used SSCLI, when you install the .NET Framework 3.5 it also installs Service Pack 1 for .NET 2.0).
In my talk, and in most of the forum discussions I have about GC, I mention that you really shouldn't "help" the GC by calling GC.Collect. A lot of people have the belief that calling GC.Collect manually will reduce the memory usage of your application and, thereby, increase performance. Calling GC.Collect will potentially decrease the memory usage, but it does so by forcing an "out of cycle" collection to occur. The reason I say "potentially" is that in this out of cycle collection there may not be any objects that are able to be reclaimed. You also suffer a performance hit during the collection cycle. Even though garbage collection cycles run very quickly, in order for the GC to do it's job properly it must freeze your applications main thread before starting the collection cycle. When the collection is finished, the thread is unfrozen. The end result of this is that you are context switching between threads and freezing your application a lot more often than you would be without the calls to GC.Collect, which ultimately will start to hurt your performance. There are times when you should call GC.Collect(), but generally it is discouraged except for debugging purposes.
So, what is the change to the GC? The change adds an overload to the GC.Collect() method:
1: void System.GC.Collect(int generation, System.GCCollectionMode mode)
According to the MSDN documentation for this overload, this "forces a garbage collection from generation zero through a specified generation, at a time specified by a GCCollectionMode value."
What this really means is that you can use the mode parameter to specify when the collection should occur. The valid values for mode are:
- Default: This is currently equivalent to Forced.
- Forced: Forces the garbage collection to occur immediately. This is the same behavior as if you called GC.Collect without specifying the mode.
- Optimized: Allows the garbage collector to determine the optimal time to reclaim objects.
Using the Default or Forced modes is really the same as calling GC.Collect(), so you shouldn't use them except in specific cases (as shown in Rico's blog post). However, the Optimized mode tells the GC "I want to do an out of cycle collection, but only if it's needed." Ultimately, the GC considers a lot of different factors in deciding this, including amount of memory considered garbage and the amount of heap fragmentation. If the GC decides a collection isn't needed, it won't run one and the call will have no effect.
One other thing to keep in mind if you use this new overload is that it doesn't guarantee that all inaccessible objects in the specified generation will be reclaimed.
Even with this new overload, I still recommend against using GC.Collect (or this overload with the Forced or Default modes) except under very specific circumstances. If you feel you absolutely must add your own calls to GC.Collect, make sure to use the overload and a mode of Optimized. Also, if you have existing code that makes use of calls to GC.Collect, you should take the time to review each of those calls to see if they are still necessary and consider changing them to call the overload with a mode of Optimized.