Вопрос по vb.net, .net, memorystream – Использование MemoryStream приводит к исключению нехватки памяти

7

сталкиваюсь с проблемами при использовании MemoryStream несколько раз.

Пример:

For Each XImage As XImage In pdfDocument.Pages(pageCount).Resources.Images
   Dim imageStream As New MemoryStream()
   XImage.Save(imageStream, System.Drawing.Imaging.ImageFormat.Jpeg)

   ' some further processing

   imageStream.Close()
   imageStream.Dispose()    
Next

Этот фрагмент кода циклически перебирает изображения на странице файла PDF. Файл может содержать до 500 страниц, скажем, 5 изображений на каждой странице. Это приводит к тысячам итераций. Проблема в том, что MemoryStream не освобождается и приводит к исключениям «Недостаточно памяти». XImage обычно имеет около 250 кБ.

Я использую здесь библиотеку Aspose.PDF для работы с PDF (XImage - класс из этой библиотеки), но это не имеет значения. Я попытался создать простой пример, в котором я просто создаю новый MemoryStream и сохраняю в нем фиктивный растровый рисунок. Это приводит к тем же проблемам.

Я также пытался использовать FileStream, а не MemoryStream, но он ведет себя так же.

Любая помощь приветствуется.

Спасибо

Иржи

Это'было бы интересно посмотреть, что произойдет, если вы нене создавать поток памяти и несохранить изображение. Может ли быть так, чтоDocument класс кэширует изображения так, чтобы они победилиНе нужно ли их реконструировать в следующий раз? Jim Mischel

Ваш Ответ

1   ответ
20

Что не освобождено, так этоадресное пространство в вашем приложении ранее занимал эту память. Там'на вашем компьютере много оперативной памяти, но ваше конкретное приложение дает сбой, потому что оно можетне найти место в немs адресная таблица, чтобы выделить больше.

Причина, по которой вы достигли предела, заключается в том, что MemoryStream перезапускает свой буфер по мере роста. Он использует байт [] для хранения своих данных, и массив по умолчанию инициализируется до определенного размера. Когда вы пишете в поток, если вы превышаете размер массива, поток использует алгоритм удвоения для выделения новых массивов. Затем информация копируется из старого массива в новый. После этого старый массив можно и будет собирать,но оно не будет уплотнено (думаю: дефрагментированный). Результат - дыры в вашем адресном пространстве, которые больше не будут использоваться. Один MemoryStream может использовать несколько массивов, в результате чего несколько дыр в памяти могут стоить всего адресного пространства, потенциально намного превышающего исходные данные.

AFAIK, в настоящее время нет способа заставить сборщик мусора сжать адресное пространство вашей памяти. Поэтому решение состоит в том, чтобы выделить большой блок, который будет обрабатывать ваше самое большое изображение, а затем снова и снова использовать один и тот же блок, так что вы не будетев конечном итоге с адресами памяти, которые не могутбыть достигнутым.

Для этого кода это означает создание потока памяти вне цикла и передачу целого числа в конструктор, чтобы он инициализировался разумным числом байтов. Вы'Это также даст вам хороший прирост производительности, так как ваше приложение внезапно перестает тратить время на частое копирование данных из одного байтового массива в другой, что означает, что это лучший вариант, даже если вы можете сжать свою таблицу адресов:

Using imageStream As New MemoryStream(307200) 'start at 300K... gives you some breathing room for larger images
    For Each XImage As XImage In pdfDocument.Pages(pageCount).Resources.Images

       'reset the stream, but keep using the same memory
       imageStream.Seek(0, SeekOrigin.Begin)
       imageStream.SetLength(0)

       XImage.Save(imageStream, System.Drawing.Imaging.ImageFormat.Jpeg)

       ' some further processing

    Next
End Using
У вас есть идея, какого размера эти изображения? Joel Coehoorn
Спасибо, я думаю, что pdfDocument невиновен. Я скопировал это с чистым System.Drawing.Bitmap, который был заполнен фиктивным изображением и сохранен в MemoryStream. Также пропущена вся дальнейшая обработка, чтобы изолировать проблему. Спасибо, в любом случае. Jiri Matejka
Тогда, вероятно, метод .Save () создает изображение в буфере за кулисами перед записью потока памяти ... но это кажется неправильным. Или я вижу, что у вас естьpageCount Индекс там. Вы по-прежнему вызываете это в цикле для каждой страницы? С 500 страницами этоДо сих пор много потоков памяти. Может быть, это должна быть функция, которая принимает поток в качестве аргумента. Последний вариант - использовать временный файл на диске. Joel Coehoorn
Это'Также возможно, что pdfDocument делает то же самое, что и создает объекты ресурсов XImage, и в этом случае вам нужно переосмыслить всю эту вещь, потому что текущий подход обречен. Joel Coehoorn

Похожие вопросы