How to prevent garbage collection from pausing my application? RRS feed

  • Question

  • (I have already posted my question on StackOverflow, but since it only resulted in discussion rather than a solution I think it's better to post it here for some more advice. Just for reference:

    The problem is that my application gets occasionally paused when in production use (.NET Framework 4.0, Server 2003 R2, WPF mixed with WinForms and native components).

    The reason could easily be determined using Debug Diag:

    mscorlib_ni!System.GC.Collect(Int32, System.GCCollectionMode)+47 
    [[InlinedCallFrame] (System.GC._Collect)] System.GC._Collect(Int32, Int32) 
    PresentationCore_ni!System.Windows.Media.Imaging.RenderTargetBitmap..ctor(Int32, Int32, Double, Double, System.Windows.Media.PixelFormat)+d9 
    WindowsFormsIntegration_ni!System.Windows.Forms.Integration.HostUtils.GetRenderTargetBitmapForVisual(Int32, Int32, System.Windows.Media.Visual)+b1 
    WindowsFormsIntegration_ni!System.Windows.Forms.Integration.HostUtils.GetBitmapForWindowsFormsHost(System.Windows.Forms.Integration.WindowsFormsHost, System.Windows.Media.Brush)+1f 
    WindowsFormsIntegration_ni!System.Windows.Forms.Integration.WindowsFormsHostPropertyMap.BackgroundPropertyTranslator(System.Object, System.String, System.Object)+109 
    WindowsFormsIntegration_ni!System.Windows.Forms.Integration.PropertyMap.RunTranslator(System.Windows.Forms.Integration.PropertyTranslator, System.Object, System.String, System.Object)+32 
    WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+53 
    WindowsBase_ni!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+42 
    [[HelperMethodFrame_PROTECTOBJ] (System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup)] System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object) 
    mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+6a 
    mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+7e 
    mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+2c 
    WindowsBase_ni!System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+63 
    WindowsBase_ni!MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+be 
    WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+53 
    WindowsBase_ni!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+42 
    WindowsBase_ni!System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)+b4 
    WindowsBase_ni!MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)+104 
    WindowsBase_ni!DomainBoundILStubClass.IL_STUB_PInvoke(System.Windows.Interop.MSG ByRef)+3c 

    So in other words, having a WinFormsHost inside your application will occasionally result in a call to GC.Collect(2) through GetRenderTargetBitmapForVisual() and MemoryPressure.Add(). Apparently, this also is a "foregorund garbage" collection, so the .NET 4.0 background garbage collection doesn't help here.

    Since it doesn't seem like I can prevent these garbage collection calls, what should I do to reduce the problem? The only idea I had so far is calling GC.Collect() manually when the application is idle (not in use) for a couple of minutes, to make sure it doesn't happen when it's in use. However, I believe this is to be considered a bad practice. Any other options?

    • Edited by flöle Tuesday, September 23, 2014 9:38 AM typo
    Tuesday, September 23, 2014 9:38 AM

All replies

  • Garbage Collection basically kicks off every time a certain number of objects are created. So your first strategy is to create less objects. I know that sounds like a 'duh' statement, but it's usually something a lot of programmers don't think about until they hit an issue like this. A generally easy thing to do is to try an eliminate the number of string objects which are created.

    Another strategy is create pools objects. If you have a way to refresh objects to an initialized state, you can create pools of them. That way they don't get created over and over.

    Tuesday, September 23, 2014 4:54 PM
  • Garbage collection algorithms always are slow.  On unix operating systems garabage collection use to to take weeks.  the better solution was to back up the disk onto tape and reload disk from tape which only took a few hours.

    If your data is linked in a tree structure you may find it quicker to create a new tree and move then non null items to the new tree instead of call the garbage collection method.


    Tuesday, September 23, 2014 5:55 PM
  • > So your first strategy is to create less objects.

    Well this sounds great in theory but may be not possible (or bad trade-off) in practice . What if you have to create many objects because you need to read a lot of data from the database?

    > If your data is linked in a tree structure you may find it quicker to create a new tree and move then non null items to the new tree instead of call the garbage collection method.

    Please check my post. I'm not calling any GC method. Problem is that WPF does it forcibly on its own and I want it to stop or make sure it happens at a more convenient point of time.

    • Edited by flöle Tuesday, September 23, 2014 6:04 PM
    Tuesday, September 23, 2014 6:00 PM
  • Hello flöle,

    For this thread, i am trying to involve someone to help look into it, it may take some time and as soon as we have any result, we will tell you.

    Thanks for your understanding.

    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Thursday, September 25, 2014 7:28 AM
  • From investigations, do you think that it pauses because of large number of unused objects that have to be freed, or because of large number of referenced objects that does not have to be freed?

    Friday, September 26, 2014 4:56 AM
  • Unfortunately I do not have any proof for that (not sure how I should verify that), but I assume that a larger number of objects has to be freed.
    Friday, September 26, 2014 10:20 AM
  • Unfortunately garbage collection comes with that tradeoff.

    See if you can reuse threads e.g. if having a threadpool will benefit you if you are starting and closing threads on yourself.

    Without deeper knowledge of your code it's hard to make assumptions on how to improve anyway.

    One thing you can try do and see if that works is manually call garbage collection yourself on regular intervals (or even better based on an internal "value" of objects used/unused or checking the memory consumption along with other things). It's a bet against what the garbager itself believes is the right time to start collecting.

    This will have side effects and overall worse performance but since it'll have to do LESS of a house-keeping it may not freeze the application as much. In other words you will probably get more freezes that last less. But you have to try and see.

    I suggest you try a memory profiler and see where your code needs improvement.

    "If there's nothing wrong with me, maybe there's something wrong with the universe!"

    Friday, September 26, 2014 11:08 AM
  • OK, thanks so far for your help. I wanted to make sure that invoking the GC at my own "pace" is not something that should be avoided at all costs. I understand that this needs some memory profiling too and may be be worse "overall", but it's worth a try and observing the effects.

    Why is it that the application is blocked at all? The docs say "background garbage collection is performed on a dedicated thread and is applicable only to generation 2 collections." and based on the source code here GC.Collect(2) is called.

    Is there anything I can do to make sure these collections happen on a background thread more often?

    • Edited by flöle Sunday, September 28, 2014 4:46 PM
    Sunday, September 28, 2014 4:35 PM
  • Garbage collection doesn't necessarily happen on a background thread. It happens on whatever thread tries to allocate more memory, but all of the currently allocation blocks are used up. So that thread will run a garbage collection.
    Monday, September 29, 2014 5:54 PM
  • WPF is a memory hog. If you want to reduce pause times try switching over to .NET 4.5 which has an improved Garbage collector which should reduce pause times in real world apps by ca. 15%. The GC will still need to pause your application threads when it compacts the managed heap. The GC has become much better at scanning for garbage concurrently with your application running.

    If you are running x86 try switching over to x64 because the GC can then use 64 bit wide registers resulting in improved memory bandwidth (nearly factor 2) to move memory blocks around. That will also reduce latency.

    If you still need more responsiveness you need to use less memory and prevent the allocation of temporary objects.

    For a more in depth analysis have a look here:

    Sunday, October 5, 2014 1:00 PM