Чтение из потока PackagePart не освобождает память - PullRequest
5 голосов
/ 29 сентября 2010

В нашем приложении мы читаем файл XPS, используя класс System.IO.Packaging.Package.Когда мы читаем из потока PackagePart, мы видим из диспетчера задач, что потребление памяти приложением возрастает.Однако когда чтение завершено, потребление памяти не падает до того, которое было до чтения из потока.

Чтобы проиллюстрировать проблему, я написал простой пример кода, который вы можете использовать на стендеОдно приложение wpf.

 public partial class Window1 : Window
 {
        public Window1()
        {
            InitializeComponent();

            _package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None);

        }

        private void ReadPackage()
        {
            foreach (PackagePart part in _package.GetParts())
            {
                using (Stream partStream = part.GetStream())
                {
                    byte[] arr = new byte[partStream.Length];
                    partStream.Read(arr, 0, (int)partStream.Length);
                    partStream.Close();
                }
            }
        }

        Package _package;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ReadPackage();      
        }
 }

Метод ReadPackage () считывает содержимое потока всех объектов PackagePart в локальный массив.В этом примере я использовал 1000-страничный XPS-документ в качестве источника пакета, чтобы легко увидеть изменение потребления памяти приложением.На моей машине потребление памяти автономным приложением начинается с 18 МБ, а после вызова метода увеличивается до 100 МБ.Повторный вызов метода может снова увеличить потребление памяти, но может снизиться до 100 МБ.Тем не менее, он больше не возвращается к 18 МБ.

Кто-нибудь испытывал это при использовании PackagePart?Или я использую это неправильно?Я думаю, что внутренняя реализация PackagePart кэширует прочитанные данные.

Спасибо!

1 Ответ

0 голосов
/ 29 сентября 2010

Вы не указываете, как вы измеряете «потребление памяти» вашего приложения, но, возможно, вы используете диспетчер задач? Чтобы лучше понять, что происходит, я предлагаю вам изучить некоторые счетчики производительности для вашего приложения. Доступны как счетчики производительности кучи .NET, так и общие памяти процессов.

Если вы действительно хотите понять детали того, как ваше приложение использует память, вы можете использовать Microsoft CLR profiler .

То, что вы видите, может быть результатом расширения кучи .NET для размещения очень большого файла. Большие объекты помещаются в кучу больших объектов (LOH), и даже если в памяти .NET собирается мусор, свободная память никогда не возвращается в операционную систему. Кроме того, объекты на LOH никогда не перемещаются во время сбора мусора, и это может привести к фрагментации LOH, исчерпывающей доступное адресное пространство, даже если имеется много свободной памяти.

Кто-нибудь испытывал это при использовании PackagePart? Или я неправильно его использую?

Если вы хотите контролировать ресурсы, используемые пакетом, вы не используете его наилучшим образом. Пакеты одноразовые и, как правило, вы должны использовать их следующим образом:

using (var package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
  // ... process the package
}

В конце оператора using ресурсы, используемые пакетом, уже освобождены или могут быть удалены сборщиком мусора.

Если вы действительно хотите сохранить _package член вашей формы, вам следует в какой-то момент позвонить Close() (или IDisposable.Dispose()), чтобы освободить ресурсы. Вызов GC.Collect() не рекомендуется и не обязательно сможет перерабатывать ресурсы, используемые пакетом. Любая управляемая память (например, буферы пакетов), доступная из _package, не будет собираться мусором, независимо от того, как часто вы пытаетесь принудительно выполнить сборку мусора.

...