Reading this old but classic document Writing High-Performance Managed Applications - A Primer, I came across following statment
The GC is self-tuning and will adjust itself according to applications memory requirements. In most cases programmatically invoking a GC will hinder that tuning. "Helping" the GC by calling GC.Collect will more than likely not improve your applications performance
I am working with applications that during a given point in time, consumes a lot of memory. When I am done in code consuming that memory, I am calling GC.Collect. If I don't do it I get Out of memory exception. This behaviour is inconistent but roughtly speaking 30% of the time, I get an out of memory. After adding GC.Collect I never got this out of memory exception. Is my action justified even though this best practice document is advising against it?
The general advice is that you should not call GC. Collect from your code, but what are the exceptions to this rule? I can only think of a few very specific cases where it may make sense to force a garbage collection.
It performs a blocking garbage collection of all generations. All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. Use this method to force the system to try to reclaim the maximum amount of available memory.
Part of what goes on in the GC is that objects in memory are generational, such that early generations are collected more frequently than others. This helps save performance by not trying to collect long-lived objects all the time.
With that in mind, two things can happen when you call GC.Collect()
yourself. The first is that you end up spending more time doing collections. This is because the normal background collections will still happen in addition to your manual GC.Collect(). The second is that you'll hang on to the memory longer, because you forced some things into a higher order generation that didn't need to go there. In other words, using GC.Collect() yourself is almost always a bad idea.
There are a few cases where the garbage collector doesn't always perform well. One of these is the large object heap. This is a special generation for objects larger than a certain size (80,000 bytes, IIRC, but that could be old now). This generation is hardly ever collected and almost never compacted. That means that over time you can end up with many sizable holes in memory that will not be released. The physical memory is not actually used and is available for other processes, but it does still consume address space within your process, of which you are limited to 2GB by default.
This is a very common source for OutOfMemory exceptions — you're not actually using that much memory, but you have all this address space taken up by holes in the large object heap. By far the most common way this happens is repeatedly appending to large strings or documents. This probably is not you, because in this scenario no amount of calls to GC.Collect() will companct the LOH, but in your case it does seem to help. However, this is the source for the vast majority of the OutOfMemory exceptions I've seen.
Another place where the garbage collector does not always perform well is when certain things cause objects to remain rooted. One example is that event handlers can prevent an object from being collected. A way around this is make sure that every +=
operation to subscribe an event has a corresponding -=
operation to unsubscribe it. But again, a GC.Collect() is unlikely to help here - the object is still rooted somewhere, and so can't be collected.
Hopefully this gives you an avenue of investigation to solve your underlying problem that causes the need to use GC.Collect() in the first place. But if not it is, of course, better to have a working program than a failing program. Anywhere I do use GC.Collect(), I would make sure the code is well documented with the reason why you need it (you get exceptions without) and the exact steps and data required to reproduce it reliably so that future programmers who may want to remove this can know for sure when it is safe to do so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With