Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advanced debugging advice in WPF GarbageCollection

Situation

We are running a large WPF application which does not release memory for quite some time. It is not a real memory leak, as the memory will be released eventually. I know that normally, this would not be considered to be a problem. Unfortunately it becomes a performance issue in conjunction with the WPF commanding infrastructure. See below for a more detailed description.

Findings

We have automated tests, that perform typical use cases. Some cases are working fine and are releasing the memory in time. Others are hogging the memory until the client is minimized, a new Window is opened or some other conditions occur that triggers a Gen2 collection.

• With ANTS we see, that the objects do not have a GC Root, but a lot of references to other objects that require finalization.

• WinDbg does not show any objects to be ready for finalization.

• Running several GC.Collect(), GC.WaitForPendingFinalizers() completely frees the memory.

• We know which UI action causes the high memory condition, but we could not identify any suspicious code.

Question

We would appreciate any advice on debugging such a problem.


WPF CommandManager Background

The WPF CommandManager holds private collection of WeakReferences (_requerySuggestedHandlers) for raising the CanExecuteChanged Event. Handling CanExecuteChanged is quite costly (especially finding the EventRoute for CanExecute, which apparently is a RoutedEvent). Anytime the CommandManager feels like requerying if commands can be executed, it iterates through this collection and calls the CanExecuteChanged event on the respective command sources.

The WeakReferences are not removed from that collection as long as there is a GC handle for the referenced object. While the object has not been collected, the CommandHelper keeps processing CanExecute events for these elements (ButtonBase or MenuItems). In case there is a lot of garbage (as in our case), this can result in an extremely large number of calls the CanExecute event handlers, which causes the application to be really laggy.

like image 627
el-duderino Avatar asked Nov 26 '12 15:11

el-duderino


People also ask

How do you fix memory garbage collection?

The usual fix is to reboot. Freeing up the operating system for reuse of the space that has been taken over by memory leaks is called garbage collection. In the past, programs have had to explicitly request storage and then return it to the system when it was no longer needed.

What causes long garbage collection time?

The root cause of high-frequency garbage collection is object churn—many objects being created and disposed of in short order.

How does C# handle garbage collection?

Garbage collection handles memory allocation safely so that no objects use the contents of another object mistakenly. The constructors of newly created objects do not have to initialize all the data fields as garbage collection clears the memory of objects that were previously released.

What garbage collection Cannot perform?

Garbage collection cannot be forced. Garbage collection cannot be forced. This indicates that Garbage Collector is an independent thread and is on a priority which is decided by the Java Virtual Machine, so you may not enforce Garbage Collection.


1 Answers

I have the same issue with one of my applications. On every opening of a window I call:

GC.GetTotalMemory(true);

This will force GC to clean the memory immediately without waiting. You can read more about this method here:

http://msdn.microsoft.com/en-us/library/system.gc.gettotalmemory.aspx

About the problem with the calls to CanExecute, I try to avoid them because of the same performance problems. Instead, I use properties in my view model and bind the IsEnabled property of the visual elements from XAML to the properties from the view model. In this way, the overall performance is improved and the CanExecute calls are gone.

I hope this will help.

like image 70
Alexandru Dicu Avatar answered Sep 27 '22 22:09

Alexandru Dicu