vb.net MemoryStream 使用导致内存不足异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17306484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MemoryStream usage leads to out of memory exception
提问by Jiri Matejka
I'am facing issues when using MemoryStream multiple times.
多次使用 MemoryStream 时遇到问题。
Example:
例子:
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
This piece of code cycles through images on a page of PDF file. The file may have up to cca 500 pages, lets say 5 images on each page. It leads to thousands of iterations. The issue is that the MemoryStream is not freed and it lead to Out of Memory exceptions. The XImage has usually around 250 kB.
这段代码在一页 PDF 文件上循环显示图像。该文件可能有多达 cca 500 页,假设每页上有 5 个图像。它导致数千次迭代。问题是 MemoryStream 没有被释放,它会导致内存不足异常。XImage 通常有大约 250 kB。
I'm using Aspose.PDF library here to work with PDF (XImage is a class from this library), but it does not matter. I tried to make a simple example where I just create a new MemoryStream and save a dummy bitmap to it. It leads to same issues.
我在这里使用 Aspose.PDF 库来处理 PDF(XImage 是这个库中的一个类),但这并不重要。我试图做一个简单的例子,我只是创建一个新的 MemoryStream 并在其中保存一个虚拟位图。它会导致同样的问题。
I also tried to use FileStream rather than MemoryStream but it behaves the same.
我也尝试使用 FileStream 而不是 MemoryStream 但它的行为相同。
Any help appreciated.
任何帮助表示赞赏。
Thanks
谢谢
Jiri
吉里
回答by Joel Coehoorn
The memory from the stream is freed. I promise you. Really, it is.
流中的内存被释放。我答应你。真的,是的。
What is not freed is the address spacein your application formerly occupied by that memory. There's plenty of ram available to your computer, but your specific application crashes because it can't find a place within it's address table to allocate any more.
未释放的是应用程序中以前被该内存占用的地址空间。您的计算机有足够的内存可用,但是您的特定应用程序崩溃了,因为它无法在其地址表中找到可以分配更多空间的位置。
The reason you hit the limit is that the MemoryStream recycles its buffer as it grows. It uses a byte[] internally to hold its data, and the array is initialized to a certain size by default. As you write to the stream, if you exceed the size of your array the stream uses a doubling algorithm to allocate new arrays. Information is then copied from the old array to new. After this, the old array can and will be collected, but it will not be compacted(think: defragged). The result is in holes in your address space that will no longer be used. One MemoryStream might use several arrays, resulting in several memory holes worth a total address space potentially much larger than the source data.
您达到限制的原因是 MemoryStream 随着它的增长而回收其缓冲区。它在内部使用一个 byte[] 来保存它的数据,并且默认情况下将数组初始化为特定大小。当您写入流时,如果超出数组的大小,流将使用加倍算法来分配新数组。然后信息从旧阵列复制到新阵列。在此之后,旧数组可以并且将被收集,但不会被压缩(想想:碎片整理)。结果是在您的地址空间中出现了不再使用的漏洞。一个 MemoryStream 可能使用多个数组,从而导致多个内存空洞的总地址空间可能比源数据大得多。
AFAIK, there is no way at this time to force the garbage collector to compact your memory address space. The solution therefore is to allocate a big block that will handle your largest image, and then reuse that same block over and over, so that you don't end up with memory addresses that can't be reached.
AFAIK,此时无法强制垃圾收集器压缩您的内存地址空间。因此,解决方案是分配一个大块来处理您最大的图像,然后一遍又一遍地重用同一块,这样您就不会得到无法访问的内存地址。
For this code, that means creating the memorystream outside of the loop, and passing an integer to the constructor so that it is initialized to a reasonable number of bytes. You'll find this also gives you a nice performance boost, as your application suddenly no longer spends time frequently copying data from one byte array to another, meaning this is the better option even if you could compact your address table:
对于此代码,这意味着在循环外创建内存流,并将整数传递给构造函数,以便将其初始化为合理的字节数。您会发现这也给您带来了不错的性能提升,因为您的应用程序突然不再花时间频繁地将数据从一个字节数组复制到另一个字节数组,这意味着即使您可以压缩地址表,这也是更好的选择:
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

