将固定文档保存到 XPS 文件会导致内存泄漏

发布于 2024-12-24 21:30:56 字数 2077 浏览 2 评论 0 原文

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

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

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

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

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

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

在这样的服务中(或一般在 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();

我尝试过的:

  • 手动垃圾收集
  • 在获取分页器之前在 myFixedDocument 的每个页面上调用 UpdateLayout() (如下面的答案中建议的那样) - 我还尝试将 myFixedDocument 直接传递到 Write() 中,即不是分页器
  • 将这些代码行放入自己的线程中并手动关闭调度员

仍然没有运气。

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

通过使用 http://msdn.microsoft.com/en-us/library/system.appdomain.aspx,内存泄漏不再影响我的服务(我说“不更长的影响”,因为它仍然会发生,但是当 AppDomain 被卸载时,所有泄漏的资源都会随之卸载)。

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

(与此相关的是,对于那些感兴趣的人来说,使用单独的 AppDomain 导致了我用来将某些 XPS 文件转换为 PDF 文件的 PDFSharp 组件中的内存泄漏。事实证明 PDFSharp 使用了全局字体缓存,该缓存在正常情况下不会增长但在使用这些 AppDomains 后,缓存不断增长,使我能够手动清除 FontDescriptorStock 和 FontDataStock,从而解决了问题。)

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

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

I have created a .NET Windows Service which performs certain actions and generates reports. These reports are XPS documents which I save in a certain directory.

Being familiar with WPF, the way I have chosen to create the reports is to instantiate a System.Windows.Documents.FixedDocument, adding FixedPage objects with content as required.

My problem is that the Service memory usage goes up and up and up over time as it runs.

At first, I went through my code rigorously, ensuring all disposable objects were disposed, etc, and other obvious memory leak candidates, but still had the problem. I then used the CLR Profiler to look at the memory usage of the Service in detail.

I found that as the service generates these FixedDocument reports, and saves them as XPS files, all the various UI elements associated with FixedDocument objects (Dispatcher, FixedPage, UIElementCollection, Visual, etc) are staying in memory.

This doesn't seem to happen when I do the same thing in my WPF apps, and so my hunch is that it has something to do with the WPF UI Dispatcher model being used outside of a WPF app.

How can I "dispose" my FixedDocument objects when using them in a service like this (or outside a WPF app in general)?

======== EDIT =========

OK, I've found that my memory leak is not specifically to do with creating/populating a FixedDocument. If I do so, but don't actually ever save it to disk as a XPS, the memory leak doesn't happen. So, my problem must be to do with the save as XPS file.

Here's my code:

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

What I've tried:

  • Manual garbage collection
  • Calling UpdateLayout() on each page of myFixedDocument before getting it's paginator (as suggested in the answer below) - I've also tried passing myFixedDocument directly into Write() i.e. not the paginator
  • Putting those lines of code in their own thread and manually shutting down Dispatchers

Still no luck.

========== WORKAROUND ==========

By isolating the above code into its own AppDomain using the general method shown in the example at http://msdn.microsoft.com/en-us/library/system.appdomain.aspx, the memory leak no longer affects my service (I say "no longer affects" because it still happens, but when the AppDomain is unloaded, all leaked resources are unloaded with it).

I would still be keen to see a real solution.

(On a related note, for those interested, using a separate AppDomain caused a memory leak in the PDFSharp component I was using to turn certain XPS files into PDF files. Turns out PDFSharp uses a global font cache that in normal circumstances doesn't grow significantly. But the cache was growing and growing after using these AppDomains. I edited the PDFSharp source code to enable me to manually clear out the FontDescriptorStock and FontDataStock, solving the issue.)

========== SOLUTION ==========

See my answer below for final solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

黑色毁心梦 2024-12-31 21:30:56

我最终找到了答案,它分为两部分。

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

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

这将清除内存中挂起的所有 Dispatcher 对象。

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

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

I eventually found an answer, which is two parts.

Firstly, after saving my XPS document to disk and closing/disposing the XpsDocument, I run the following line of code:

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();
}
浅黛梨妆こ 2024-12-31 21:30:56

this 看来,您必须致电.UpdateLayout() 至少一次以避免内存泄漏

From this, it looks like you have to call .UpdateLayout() at least once to avoid memory leak

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文