MemoryStream使用导致内存不足异常



我在多次使用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文件页面上的图像之间循环。该文件可能有多达cca 500页,比方说每页上有5个图像。它导致了数千次迭代。问题是MemoryStream没有被释放,这会导致内存不足异常。XImage通常大约有250 kB。

我在这里使用Aspose.PDF库来处理PDF(XImage是这个库中的一个类),但这并不重要。我试着做一个简单的例子,创建一个新的MemoryStream并保存一个伪位图。这会导致同样的问题。

我也尝试使用FileStream而不是MemoryStream,但它的行为相同。

感谢您的帮助。

感谢

Jiri

流中的内存被释放。我向你保证。真的,是的。

没有释放的是应用程序中以前被该内存占用的地址空间。你的计算机有很多可用的ram,但你的特定应用程序会崩溃,因为它在地址表中找不到可以分配的位置。

达到极限的原因是MemoryStream在增长时会回收其缓冲区。它在内部使用字节[]来保存数据,默认情况下,数组初始化为特定大小。在向流写入时,如果超出了数组的大小,流将使用加倍算法来分配新的数组。然后将信息从旧阵列复制到新阵列。之后,旧数组可以并且将被收集,但不会被压缩(想想:defraged)。结果是程序的虚拟地址表中的漏洞不再足够大,无法容纳MemoryStream缓冲区。一个MemoryStream可能使用多个数组,从而导致多个内存漏洞,总地址空间可能比源数据大得多。

AFAIK,此时无法强制垃圾收集器压缩内存地址空间。因此,解决方案是分配一个可以处理最大图像的大块,然后反复使用同一块,这样就不会出现无法访问的内存地址。

对于这段代码,这意味着在循环之外创建MemoryStream,并将一个整数传递给构造函数,以便将其初始化为合理数量的字节。你会发现这也会给你带来很好的性能提升,因为你的应用程序突然不再花时间频繁地将数据从一个字节数组复制到另一个字节阵列,这意味着即使你可以压缩地址表,这也是更好的选择:

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

最新更新