10

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

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

我的问题是服务内存使用量随着时间的推移不断上升。

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

我发现当服务生成这些报告并将它们保存为 XPS 文件时,与对象(、、、、等)FixedDocument关联的所有各种 UI 元素都保留在内存中。FixedDocumentDispatcherFixedPageUIElementCollectionVisual

当我在我的 WPF 应用程序中做同样的事情时,这似乎不会发生,所以我的直觉是它与在 WPF 应用程序之外使用的 WPF UI Dispatcher 模型有关。

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

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

好的,我发现我的内存泄漏与创建/填充 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() ie 而不是分页器
  • 将这些代码行放在自己的线程中并手动关闭调度程序

仍然没有运气。

========== 解决方法 ==========

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

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

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

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

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

4

2 回答 2

18

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

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

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

This gets rid of all the Dispatcher objects hanging around in memory.

While the above sorts out most of the memory issues, I noticed there were still FixedPage objects along with other UI objects still in memory. Manually clearing out my FixedDcoument seems to get rid of them:

foreach (var fixedPage in FixedDocument.Pages.Select(pageContent => pageContent.Child)) {
   fixedPage.Children.Clear();
}
于 2012-01-11T22:57:19.147 回答
0

由此看来,您必须至少调用一次 .UpdateLayout() 以避免内存泄漏

于 2012-01-05T12:31:37.830 回答