将FixedDocument保存到XPS文件会导致内存泄漏

时间:2012-01-05 12:24:09

标签: wpf memory-leaks dispatcher xpsdocument fixeddocument

我创建了一个.NET Windows服务,它执行某些操作并生成报告。这些报告是我保存在某个目录中的XPS文档。

熟悉WPF,我选择创建报告的方式是实例化System.Windows.Documents.FixedDocument,根据需要添加包含内容的FixedPage个对象。

我的问题是服务内存使用率随着时间的推移而不断上升和下降。

起初,我严格检查了我的代码,确保所有一次性物品都被丢弃等,以及其他明显的内存泄漏候选者,但仍然存在问题。然后,我使用CLR Profiler详细查看了服务的内存使用情况。

我发现,当服务生成这些FixedDocument报告并将其保存为XPS文件时,与FixedDocument个对象关联的所有各种UI元素(DispatcherFixedPageUIElementCollectionVisual等等留在记忆中。

当我在WPF应用程序中执行相同操作时似乎没有发生这种情况,因此我的预感是它与在WPF应用程序之外使用的WPF UI Dispatcher模型有关。

在这样的服务中(或者在WPF应用程序之外)使用它们时,如何“处置”我的FixedDocument个对象?

========编辑=========

好的,我发现我的内存泄漏并不是专门用于创建/填充FixedDocument。如果我这样做,但实际上并没有将其作为XPS保存到磁盘,则不会发生内存泄漏。所以,我的问题必须与保存为XPS文件有关。

这是我的代码:

var paginator = myFixedDocument.DocumentPaginator;
var xpsDocument = new XpsDocument(filePath, FileAccess.Write);
var documentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDocument);                         
documentWriter.Write(paginator);
xpsDocument.Close();

我尝试了什么:

  • 手动垃圾收集
  • 在获取它的分页符之前在UpdateLayout()的每个页面上调用myFixedDocument(如下面的答案所示) - 我也尝试将myFixedDocument直接传递到Write()即不是分页器
  • 将这些代码行放在自己的线程中并手动关闭Dispatchers

仍然没有运气。

==========替代方法==========

通过使用http://msdn.microsoft.com/en-us/library/system.appdomain.aspx示例中显示的常规方法将上述代码隔离到自己的AppDomain中,内存泄漏不再影响我的服务(我说“不再影响”,因为它仍然会发生,但是当卸载AppDomain,所有泄露的资源都随之卸载。)

我仍然希望看到一个真正的解决方案。

(相关说明,对于那些感兴趣的人,使用单独的AppDomain导致我正在使用的PDFSharp组件中的内存泄漏将某些XPS文件转换为PDF文件。结果PDFSharp使用全局字体缓存,在正常情况下不会使用这些AppDomains后,缓存增长和增长。我编辑了PDFSharp源代码,使我能够手动清除FontDescriptorStock和FontDataStock,解决了这个问题。)

==========解决方案==========

请参阅下面的答案以获得最终解决方案。

2 个答案:

答案 0 :(得分:17)

我最终找到了答案,这是两部分。

首先,在将我的XPS文档保存到磁盘并关闭/部署XpsDocument之后,我运行以下代码行:

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.SystemIdle, new DispatcherOperationCallback(delegate { return null; }), null);

这消除了内存中所有的Dispatcher个对象。

虽然上面排除了大部分内存问题,但我注意到仍有固定页面对象以及其他UI对象仍在内存中。手动清除我的FixedDcoument似乎摆脱了它们:

foreach (var fixedPage in FixedDocument.Pages.Select(pageContent => pageContent.Child)) {
   fixedPage.Children.Clear();
}

答案 1 :(得分:0)

this开始,您似乎必须至少调用一次.UpdateLayout()以避免内存泄漏