Why does background garbage collection sometimes pause my application and how can I prevent it?

We have a large (= may need 200 - 500 MB or more memory during the day) WPF application that is used for several hours a day. Sometimes the application freezes for no apparent reason. Analysis of the process dumps shows that garbage collection is active and the reason for the suspended application.

We are using .NET 4.0, and as far as I understand, the new " background garbage collection " should reduce the time the whole process is blocked for garbage collection (foreground garbage collection).

However, even if the pauses do not occur as often, they interrupt the workflow when they take more than a few seconds (this is the case).

This leads me to the following questions:

  • I read that background garbage collection is not used by default in a server environment. Our application is indeed running on a server operating system (Windows Server 2003 R2 x64), even though it is a client application (not a server application). Does this mean that background garbage collection is not used? Or does this only apply to /ASP.NET services?

  • Assuming background garbage collection is indeed enabled, how can I prevent foreground collections from being collected too often / too many? My current approach would be to detect periods of downtime in the application (for example, the application has not been used for 5 minutes or more) and initiate an enforced garbage collection so that it does not happen at a later point in time when the application is in use.

Debug Diag process dump information: enter image description here

Stack trace:

mscorlib_ni!System.GC.Collect(Int32, System.GCCollectionMode)+47 
[[InlinedCallFrame] (System.GC._Collect)] System.GC._Collect(Int32, Int32) 
PresentationCore_ni!MS.Internal.MemoryPressure.ProcessAdd()+1d0 
PresentationCore_ni!MS.Internal.MemoryPressure.Add(Int64)+39 
PresentationCore_ni!System.Windows.Media.SafeMILHandleMemoryPressure..ctor(Int64)+43 
PresentationCore_ni!System.Windows.Media.SafeMILHandle.UpdateEstimatedSize(Int64)+38 
PresentationCore_ni!System.Windows.Media.Imaging.RenderTargetBitmap.FinalizeCreation()+df 
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.GetBitmapForFrameworkElement(System.Windows.FrameworkElement)+89 
WindowsFormsIntegration_ni!System.Windows.Forms.Integration.HostUtils.GetBitmapForTransparentWindowsFormsHost(System.Windows.Forms.Integration.WindowsFormsHost)+4b 
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 
WindowsFormsIntegration_ni!System.Windows.Forms.Integration.WindowsFormsHost.ArrangeOverride(System.Windows.Size)+277 
PresentationFramework_ni!System.Windows.FrameworkElement.ArrangeCore(System.Windows.Rect)+8e3 
PresentationCore_ni!System.Windows.UIElement.Arrange(System.Windows.Rect)+385 
PresentationCore_ni!System.Windows.ContextLayoutManager.UpdateLayout()+2b5 
PresentationCore_ni!System.Windows.ContextLayoutManager.UpdateLayoutCallback(System.Object)+19 
PresentationCore_ni!System.Windows.Media.MediaContext+InvokeOnRenderCallback.DoWork()+10 
PresentationCore_ni!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()+76 
PresentationCore_ni!System.Windows.Media.MediaContext.RenderMessageHandlerCore(System.Object)+8a 
PresentationCore_ni!System.Windows.Media.MediaContext.AnimatedRenderMessageHandler(System.Object)+6e 
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.DispatcherOperation.InvokeImpl()+8d 
WindowsBase_ni!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(System.Object)+38 
mscorlib_ni!System.Threading.ExecutionContext.runTryCode(System.Object)+51 
[[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.DispatcherOperation.Invoke()+68 
WindowsBase_ni!System.Windows.Threading.Dispatcher.ProcessQueue()+15e 
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!MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)+7d 
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 
[[InlinedCallFrame]] 
WindowsBase_ni!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)+c1 
WindowsBase_ni!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)+49 
PresentationFramework_ni!System.Windows.Application.RunDispatcher(System.Object)+5b 
PresentationFramework_ni!System.Windows.Application.RunInternal(System.Windows.Window)+74 
PresentationFramework_ni!System.Windows.Application.Run(System.Windows.Window)+2b 

      

+3


source to share


1 answer


You can change the GC mode using app.config. The Windows server is disabled by default. The rationale is pretty simple - background GC means less noticeable latency (optimal for custom applications), while foreground GC has better overall throughput.

However, if the GC collection is long enough to cause a noticeable hang, you are probably doing something wrong. This is not really about the amount of memory, but rather that it is the result of inefficient handling of such memory. The biggest offender I've seen is doing it by GC.Collect

hand - this basically kills all the performance gains of the generator GC (among other optimizations), which is extremely important. The Debug Diag snippet seems to indicate that this is actually the case - you seem to be manually initiating the collection; but I have never worked with this tool so it might be false.

200-500 MiB is not much, of course. The critical point is how easy it is to collect. It depends on how the objects are tied to different generations, how many objects there are (not their total size, although this obviously also plays a role), memory locality, and much more.

Somewhat counter-intuitive, in .NET it is usually a bad idea to force yourself to reuse objects and do similar optimizations like in C ++. This will most likely result in degraded GC performance as it degrades memory locality and memory heap splitting.

The key point remains - the profile. Attach Concurrency Visualizer and CLRProfiler. They will tell you how much GC works and can help you understand why.

And repeat - don't use GC.Collect

. I've never seen it lead to performance gains and I've seen it kill GC performance many times over. The only sensible use case I've seen for it was in tests, which are actually simple enough to take advantage of forced build. If you're not trying to do these micro-optimizations, the heap can actually be almost as fast as the stack (mainly working on restricted objects - a handy shortcut). NET GC is actually pretty good.



EDIT

Based on the additional information, I am afraid this could be a serious problem when doing a lot of background work in a WPF application. If you split the background work into an additional process and only threaten the WPF application as a GUI interface to some underlying service, you can work around the whole problem. Obviously it won't be very easy to implement ...

Another option is to try to limit WPF garbage as much as possible - the less data WPF will run, the less slanted it will be called GC.Collect(2)

.

The setup app.config

to make the GC work like it does on non-server windows is pretty straightforward:

<configuration>
 <runtime>
  <gcServer enabled="false" />
  <gcConcurrent enabled="true" />
 </runtime>
</configuration>

      

This should help speed up the time when the UI is unresponsive - when there are multiple processor cores.

+4


source







All Articles