WPF垃圾回收的高级调试建议

13

情况

我们正在运行一个大型的WPF应用程序,它不会在相当长的时间内释放内存。这不是真正的内存泄漏,因为内存最终会被释放。我知道通常情况下,这不会被认为是一个问题。不幸的是,与WPF命令基础设施结合使用时,它会成为性能问题。请参见下面的详细描述。

结果

我们有一些自动化测试,执行典型用例。有些情况下工作正常,并及时释放内存。其他情况下会占用内存,直到客户端最小化、打开新窗口或发生触发Gen2收集的其他条件。

• 使用ANTS,我们看到对象没有GC Root,但有许多引用其他需要完成的对象。

• WinDbg没有显示任何准备完成的对象。

• 运行几个GC.Collect()GC.WaitForPendingFinalizers()完全释放内存。

• 我们知道哪个UI操作导致高内存条件,但我们无法识别任何可疑的代码。

问题

我们将非常感谢任何关于调试此类问题的建议。


WPF CommandManager的背景

WPF CommandManager持有WeakReferences (_requerySuggestedHandlers)的私有集合,用于引发CanExecuteChanged事件。处理CanExecuteChanged是相当昂贵的(特别是查找CanExecute的EventRoute,它明显是一个RoutedEvent)。每当CommandManager感觉需要重新查询命令是否可执行时,它都会遍历此集合,并在相应的命令源上调用CanExecuteChanged事件。

只要引用的对象有GC句柄,就不会从该集合中移除WeakReferences。只要对象没有被收集,CommandHelper将继续处理这些元素(ButtonBase或MenuItems)的CanExecute事件。如果垃圾很多(如我们的情况),这可能导致CanExecute事件处理程序调用次数极其庞大,从而导致应用程序非常卡顿。


1
你正在使用哪个版本的WPF?3.x版本被认为存在一些内存泄漏问题 - vcsjones
我们正在运行4.0版本,但文章提到WPF 4.0仍存在Bug KB981107,没有热修复。我会去看一下。同时,我们可以在一个示例应用程序中隔离问题并重现它。我们的DevExpress组件似乎有些问题。感谢提供链接! - el-duderino
2个回答

5

我有一个应用程序也遇到了同样的问题。每次打开一个窗口时,我都会调用:

GC.GetTotalMemory(true);

这将强制GC立即清理内存,无需等待。您可以在此处阅读更多关于此方法的信息:

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

关于调用CanExecute的问题,我尽量避免这样做,因为会出现性能问题。相反,我在我的视图模型中使用属性,并将XAML中的可视元素的IsEnabled属性绑定到视图模型中的属性。通过这种方式,可以提高整体性能并消除CanExecute的调用。
我希望这可以帮助到您。

0

尝试使用CLRProfiler,这是下载链接。它显示分配、释放和存活的事件处理程序。我相信你可以通过使用这个工具追踪到根本原因。 书籍《高级.NET调试》列出了一些很好的调试工具,你可以阅读它以获取一些帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接