none
Why do I need to call GC.Collect to keep my application's memory usage from growing without bound? RRS feed

  • Question

  • When I use perfmon to monitor the size of the managed heap in my application, it appears to steadily grow. I can see (via perfmon) that Generation2 GC runs periodically but I never see a corresponding drop in the Gen2 heap size. Eventually when the Gen2 heap gets to about a GByte, the application bogs down to the point of being unusable.

    At first I thought I had a memory leak, but I noticed that explicit calls to GC.Collect() would bring the Gen2 heap size back down under 100K. With a periodic call to GC.Collect, the application runs fine.

    Another interesting point is that even without the GC.Collect call in place, calls to System.GC.GetTotalMemory(false) never report over 100K (even when perfmon shows the "# of bytes in all heaps" to be growing up to the 1Gbyte point).

    So the question is: what does a call to GC.Collect do that the automatic GC isn't doing? And what about my application is preventing it from happening?

    This is a rather large application running under XP64 with a WPF ui and many threads, both managed and unmanaged.


    Brad Pechacek
    Monday, December 13, 2010 7:40 PM

Answers

  • > calls to System.GC.GetTotalMemory(false) never report over 100K

    One thing I can think of is that unmanaged resources are being leaked.  GC also kicks off finalizers, so such unmanaged resources might be reclaimed too.  Check that you are calling Dispose as required.  That being said, that would not explain a large gen 2...

    > System.GC.GetTotalMemory(false) never report over 100K (even when perfmon shows the "# of bytes in all heaps" to be growing up to the 1Gbyte point).

    I don't think that makes sense.

    One thing you might want to look into is using a .NET memory profiling tool, if you have not already.

    > Eventually when the Gen2 heap gets to about a GByte, the application bogs down to the point of being unusable.

    Why?  Because of paging?  If you have enough memory (and you might since you said 64-bit), the large gen 2 might not be unreasonable.  If there is not paging, this again points to leaking of resources that are not simply GC memory resources.

     

    • Proposed as answer by Konrad Neitzel Tuesday, December 14, 2010 6:15 AM
    • Marked as answer by Cookie Luo Thursday, December 16, 2010 8:51 AM
    Tuesday, December 14, 2010 4:28 AM

All replies

  • > calls to System.GC.GetTotalMemory(false) never report over 100K

    One thing I can think of is that unmanaged resources are being leaked.  GC also kicks off finalizers, so such unmanaged resources might be reclaimed too.  Check that you are calling Dispose as required.  That being said, that would not explain a large gen 2...

    > System.GC.GetTotalMemory(false) never report over 100K (even when perfmon shows the "# of bytes in all heaps" to be growing up to the 1Gbyte point).

    I don't think that makes sense.

    One thing you might want to look into is using a .NET memory profiling tool, if you have not already.

    > Eventually when the Gen2 heap gets to about a GByte, the application bogs down to the point of being unusable.

    Why?  Because of paging?  If you have enough memory (and you might since you said 64-bit), the large gen 2 might not be unreasonable.  If there is not paging, this again points to leaking of resources that are not simply GC memory resources.

     

    • Proposed as answer by Konrad Neitzel Tuesday, December 14, 2010 6:15 AM
    • Marked as answer by Cookie Luo Thursday, December 16, 2010 8:51 AM
    Tuesday, December 14, 2010 4:28 AM
  • Using msdbg and sos.dll and think I figured out what is going on.

    When I stopped the app, it looked like the main thread was in GC, so I assume when the app bogs down it is spending most of its time in gc.

    I believe the culprit here is heap fragmentation. GetTotalMemory must report the portion of the heap that is not free. It appears that 90% of the heap was not being used due to fragmentation. The !dumpheap command showed that most of the free portions of the heap were followed by System.EventHandler objects. I am assuming this means that these objects are pinned. Sure enough I tracked down a spot in our code where we were leaking a System.EventHandler object. The amount of memory lost due to this leak was miniscule, but because of the pinning, they were causing the heap to fragment.

     


    Brad Pechacek
    Tuesday, December 14, 2010 5:49 PM
  • Interesting.  Glad you were able to figure it out.

     

    Wednesday, December 15, 2010 12:40 AM