none
GC.Collect() solves OutOfMemoryException

    Question

  •  

    I have a "memroy leak" in a windows forms 2.0 application, i.e. I can see the memory consumtion (working set of the process in question) grow over time until finally after some hours an OutOfMemoryException is thrown.

     

    As I started investigating this I added a GC.Collect() that was called frequently by a timer (once every second) while the appliction runs. To my surprise this actually "solved" that memory leak.

     

    This doesn't make sense. Oviously the garbage collection never collected object that where actually collectable (not refenced anymore). What can cause this? How come that calling GC.Collect() manually collects so many objects that the GC didn't collect automatically? What does GC.Collect() different from the automatic garbage collection?

    Tuesday, October 30, 2007 12:10 PM

Answers

  • Sure enough you should do all that fun stuff, but as for why it's happening _maybe_ you're seeing the difference between a generation 1 or 0 collection and a generation 2 collection.  If your objects are either large (so they get allocated on the large object heap) or long lived (so they survive to generation 2), then the most used garbage collections won't touch them (heaps 0 and 1 get collected more often because it's cheaper to do).  When you call GC.Collect() you force a generation 2 collection, which clears up generation 2 objects and the large object heap (an expensive collection).  I'm not sure why your application wouldn't figure that out before an OOM, but if your objects are truly large then it's possible they take the garbage collector by surprise and don't give it time to react to the memory pressure that would trigger an OOM.

     

    There are various tools you can use to investigate your heaps and memory usage - sos is the one I typically use and is pretty good at telling you what's going on in your heaps.

     

    Tuesday, October 30, 2007 2:05 PM

All replies

  • If you suspect you have a memory leak you really should be using a tool like CLR Profiler or BoundsChecker. I'm assuming that you are using Task Manager to watch the working set memory, which is a misleading number at best.

     

    Rather than simply calling GC.Collect inside a timer loop, you really need to look at the code and make sure that you are properly disposing of any objects that implement IDisposable. You should be placing these inside a using block or a try/finally block. Also, if you have an custom objects that you have written (or have source code to) that are using any unmanaged resources or doing any type of P/Invoke calls, you need to make sure that the Dispose methods are written properly and are cleaning up the unmanaged resources.

     

    You can take a look at the following two articles for more information:

    http://www.codeproject.com/useritems/idisposable.asp

    http://www.codeproject.com/dotnet/safehandle.asp

     

    The problem with calling GC.Collect like this is that you are actually hurting the performance of the GC. Any time the GC runs a collection cycle it must suspend your applications main thread in order for it to mark objects on the heap. The more times you call GC.Collect, the more time you spend in a GC cycle and the more time your application is in a suspended state.

     

    Most likely, what you are seeing is due to the fact that you are constantly running GC cycles. Under normal conditions the GC will only run a collection cycle when it determines that there isn't enough memory in the heap for the next allocation. In reality, you're probably just getting lucky on the fact that calling GC.Collect like this is "solving" your problem...it's really just masking it.

     

     

    Tuesday, October 30, 2007 1:30 PM
  • I am aware of the fact that calling GC.Collect is probably a bad idea I just tried this out of curiosity while profiling the application with a profiler (AQTime).  Also I don't think that there are any issues with the IDisosable pattern in my case. Except the main Form there are almost no objects involved that have managed ressources to dispose. Also if there would be an issue with undisposed managed ressources (due to not calling IDisposable.Dispose) a call to GC.Collect wouldn't have any effect, would it? The managed ressources would still not be disposed and hence its objects not collectable.

     

    My question was more into the direction on how something like I described is possible in the first place. How come that calling GC.Collect() manually collects so many objects that the GC didn't collect automatically? What does GC.Collect() different from the automatic garbage collection thread? COuld it be that for some reason the GC THread didn't get a chance to run at all?  If the GC kicks in when when it determines that there isn't enough memory in the heap for the next allocation, well why didn't it collect those objects BEFORE that OutOfMemoryException occured. If I can collect them manually why can't the garbage collector thread?

    Tuesday, October 30, 2007 1:58 PM
  • Sure enough you should do all that fun stuff, but as for why it's happening _maybe_ you're seeing the difference between a generation 1 or 0 collection and a generation 2 collection.  If your objects are either large (so they get allocated on the large object heap) or long lived (so they survive to generation 2), then the most used garbage collections won't touch them (heaps 0 and 1 get collected more often because it's cheaper to do).  When you call GC.Collect() you force a generation 2 collection, which clears up generation 2 objects and the large object heap (an expensive collection).  I'm not sure why your application wouldn't figure that out before an OOM, but if your objects are truly large then it's possible they take the garbage collector by surprise and don't give it time to react to the memory pressure that would trigger an OOM.

     

    There are various tools you can use to investigate your heaps and memory usage - sos is the one I typically use and is pretty good at telling you what's going on in your heaps.

     

    Tuesday, October 30, 2007 2:05 PM