Сохранение FixedDocument в файл XPS приводит к утечке памяти - PullRequest
9 голосов
/ 05 января 2012

Я создал службу .NET Windows, которая выполняет определенные действия и создает отчеты.Эти отчеты являются документами XPS, которые я сохраняю в определенном каталоге.

Зная WPF, я выбрал для создания отчетов создание экземпляра System.Windows.Documents.FixedDocument, добавляя FixedPage объекты с содержимым по мере необходимости..

Моя проблема заключается в том, что использование служебной памяти с течением времени увеличивается и увеличивается с течением времени.

Сначала я тщательно просмотрел свой код, обеспечивбыли удалены все одноразовые предметы и т. д., а также другие очевидные кандидаты на утечку памяти, но проблема все еще была.Затем я использовал CLR Profiler для подробного анализа использования памяти Сервисом.

Я обнаружил, что, поскольку служба генерирует эти отчеты FixedDocument и сохраняет их в виде файлов XPS, все различные элементы пользовательского интерфейса связываютсяс FixedDocument объектами (Dispatcher, FixedPage, UIElementCollection, Visual и т. д.) остаются в памяти.

Похоже, этого не происходит, когда я делаю то же самое в моемПриложения WPF, и поэтому я догадываюсь, что это как-то связано с моделью диспетчера пользовательского интерфейса WPF, которая используется вне приложения WPF.

Как я могу "распоряжаться" моими 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 перед получением его paginator (как предложено в ответе ниже) - я также попытался передать myFixedDocument непосредственно в Write(), т.е. не в paginator
  • Поместить эти строки кода в их собственный поток и вручную отключить диспетчеры

Все еще не повезло.

========== ВРЕМЕННОЕ РЕШЕНИЕ==========

Путем выделения вышеуказанного кода в свой собственный домен приложений с использованием общего метода, показанного в примере в http://msdn.microsoft.com/en-us/library/system.appdomain.aspx,, утечка памяти больше не влияет на мой сервис (яскажем «больше не влияет», потому что это все еще происходит, но когда домен приложения выгружен, все просочившиеся ресурсы выгружаются вместе с ним).

Я все равно хотел бы увидеть реальное решение.

(Для связанного примечания, для заинтересованных, использование отдельного AppDomain вызвало утечку памяти вКомпонент PDFSharp, который я использовал для преобразования некоторых файлов XPS в файлы PDF.Оказывается, PDFSharp использует глобальный кэш шрифтов, который в нормальных условиях существенно не растет.Но кэш рос и рос после использования этих доменов приложений.Я отредактировал исходный код PDFSharp, чтобы я мог вручную очистить FontDescriptorStock и FontDataStock, решив проблему.)

========== РЕШЕНИЕ ==========

См. Мой ответ ниже для окончательного решения.

Ответы [ 2 ]

17 голосов
/ 12 января 2012

В конце концов я нашел ответ, состоящий из двух частей.

Во-первых, после сохранения моего XPS-документа на диск и закрытия / удаления XpsDocument я запускаю следующую строку кода:

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

Это избавляет от всех объектов Dispatcher, висящих в памяти.

Хотя вышеперечисленное решает большинство проблем с памятью, я заметил, что все еще существуют объекты FixedPage вместе с другими объектами пользовательского интерфейса.в памяти.Очистка моего FixedDcoument вручную, кажется, избавляет от них:

foreach (var fixedPage in FixedDocument.Pages.Select(pageContent => pageContent.Child)) {
   fixedPage.Children.Clear();
}
0 голосов
/ 05 января 2012

С это , похоже, вам нужно хотя бы один раз вызвать .UpdateLayout (), чтобы избежать утечки памяти

...