填充 MemoryStream 时出现 OutOfMemoryException:16GB 系统上的 256MB 分配
Posted
技术标签:
【中文标题】填充 MemoryStream 时出现 OutOfMemoryException:16GB 系统上的 256MB 分配【英文标题】:OutOfMemoryException while populating MemoryStream: 256MB allocation on 16GB system 【发布时间】:2013-03-13 18:26:59 【问题描述】:我在我的开发 IIS 服务器(来自 VS2010 IDE)上运行以下方法,在 64 位 Windows 7 机器上安装了 16GB 内存:
public static MemoryStream copyStreamIntoMemoryStream(Stream stream)
long uiLen = stream.Length;
byte[] buff = new byte[0x8000];
int nSz;
MemoryStream ms = new MemoryStream();
try
while ((nSz = stream.Read(buff, 0, buff.Length)) != 0)
ms.Write(buff, 0, nSz);
finally
Debug.WriteLine("Alloc size=" + ms.Length);
return ms;
我在这一行得到System.OutOfMemoryException
:
ms.Write(buff, 0, nSz);
分配 268435456 字节时抛出:
分配大小=268435456
这是 0x10000000 或 256 MB。所以我想知道是否需要设置一些全局设置才能使其正常工作?
这是项目配置设置的屏幕截图:
【问题讨论】:
如果您知道MemoryStream
constructor 的初始大小大约有多大,您可以尝试设置一个更大的初始大小吗?
@JonathonReinhart:刚刚尝试过。 stream.Length
设置为 0。这是一个 ZIP 存档流,所以我猜它没有预先提供。
问题是 MemoryStream 每次超出时都会将其内部缓冲区的大小翻倍。所以也许你可以尝试设置一个 512MB 或 1GB 的初始大小。
乔恩,我尝试设置更大的尺寸,但发现这只是解决了问题:它没有在扩展时死亡,而是在新版本中提前死亡。这真的很令人沮丧,因为在我的项目中它发生在 10 MB,因为我从一个流向另一个流馈送。
这里codeproject.com/Articles/348590/A-replacement-for-MemoryStream和这里memorytributary.codeplex.com的MemoryStream的可能替代品(我自己没试过。)
【参考方案1】:
如果您使用默认的 VS 开发服务器,则您正在 x86/32 位进程中运行代码。如果您使用的是完整的 IIS - 很可能在 IIS 中,特定的 AppPool 被配置为在 x86(32 位模式)下运行,因此地址空间非常有限(2GB,除非您将应用程序标记为大地址感知)。
如果是 IIS,请确保您已将应用轮询配置为运行 x64(不确定默认值是什么)。确保您的代码目标设置为 AnyCPU 或 x64。
对于独立的 C# 应用程序 - 默认情况下,它们使用 x86 或 AnyCPU/Prefer x86 编译 - 将目标平台更改为 x64。
要获得对 IIS 的 x64 支持,您可以install full IIS 或从Download IIS 8.0 Express 安装 IIS Express 8.0(Windows 7 附带的 7.5 仅为 32 位)。
旁注:
如果您刚刚安装了完整的 IIS,请确保更新您的解决方案以将 IIS 用于站点的主机。 我没有机器来检查是否有任何额外的步骤需要使用 IIS Express 的 x64 支持。看看这个问题 - Can't get IIS Express 8 beta to run website as 64-bit process - 因为它可能会给你一些想法。 您也可以尝试根据此处的建议构建自己的 x64 版本的开发服务器 - Is Visual Studio 2010 WebDev WebServer (Cassini) 64-bit compatible?【讨论】:
我刚刚检查过(见上面的截图),它似乎被编译为“Any CPU”。是 x64 的意思吗? @c00000fd,好的。MemoryStream
应该稍后会失败(大约从 1GB 增长)......所以很奇怪你这么早就在 64 位进程中遇到错误......
是的。这就是为什么我把它贴在这里。我可以检查任务管理器以确保它以 x64 运行。它在asp.net开发服务器中运行的是什么进程?
我想是WebDev.WebServer40.EXE
,不是吗?如果是这样,它确实作为 32 位进程运行。
哦,哇。谢谢。他们还没有编写 64 位版本,这真是太蹩脚了。我可以在不是服务器版本的 Windows 7 上安装 IIS 吗?【参考方案2】:
简答 - 开发服务器是 32 位进程。
“为什么只有 256Mb?”的长答案
首先,让我们了解它是如何工作的。
MemoryStream 具有内部 byte[] 缓冲区来保存所有数据。它不能预测这个缓冲区的确切大小,所以它只是用一些初始值来初始化它。
Position 和 Length 属性不反映实际缓冲区大小 - 它们是反映写入字节数的逻辑值,很容易小于实际物理缓冲区大小。
当这个内部缓冲区不能容纳所有数据时,应该“重新调整大小”,但在现实生活中,这意味着创建新缓冲区大小是前一个缓冲区的两倍,然后复制数据从旧缓冲区到新缓冲区。
因此,如果您的缓冲区长度为 256Mb,并且您需要写入新数据,这意味着 .Net 需要找到另一个 512Mb 数据块 - 其余所有数据都已到位,因此堆应位于当您收到 OutOfMemory 时,内存分配时至少有 768Mb。
另外请注意,默认情况下,.Net 中的单个对象(包括数组)的大小不能超过 2Gb。
好的,这是模拟正在发生的事情的示例:
byte[] buf = new byte[32768 - 10];
for (; ; )
long newSize = (long)buf.Length * 2;
Console.WriteLine(newSize);
if (newSize > int.MaxValue)
Console.WriteLine("Now we reach the max 2Gb per single object, stopping");
break;
var newbuf = new byte[newSize];
Array.Copy(buf, newbuf, buf.Length);
buf = newbuf;
如果它内置 x64/AnyCPU 并从控制台运行 - 一切正常。
如果它跨 x86 构建 - 它在控制台中失败。
如果你说 Page_Load,内置 x64,并从 VS.Net Web 服务器打开 - 它会失败。
如果您对 IIS 执行相同操作 - 一切正常。
希望这会有所帮助。
【讨论】:
最重要的是,分配的 512 MB 必须是连续的内存块。并不是说 32 位没有足够的可用地址空间;只是地址空间是碎片化的。使用多个较小字节数组的 Stream 实现可能更接近 2 GB 的限制。 感谢您的解释。不过,在另一个主题上——为什么操作系统不能对其内部 RAM 映射进行碎片整理,比如说,当计算机不使用时?因为它不能分配 512MB 的数据太可笑了。什么,是Windows 95吗?有时我想知道微软是否在 Windows 7 中做了什么特别的事情...... 它没有你想象的那么糟糕)例如阅读这个答案***.com/a/5243503/2170171 在大多数情况下,512Mb 将在 16Gb RAM 机器上分配而没有任何问题,这里是这种特定情况***.com/questions/686950/…跨度> 我知道这是旧的,但我想知道 Stream.CopyTo() 方法是否不能解决小于 int.MaxValue 的流长度的内存分配问题?在这种情况下,以下代码可能不太容易出现 OutOfMemory 异常:if (int.MaxValue >= memoryStream.Length) MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); memoryStream.Capacity = (int) memoryStream.Length; // Optional but helps eliminate null padding.
@Teorist default CopyTo
实现与所讨论的代码完全相同 - 在缓冲区中读取源并以块的形式写入目标,从而导致内部缓冲区重新分配。你可以看到 Stream
在此处引用源 github.com/Microsoft/referencesource/blob/master/mscorlib/… 为了避免缓冲区重新分配,我们需要 1)能够提前知道源流长度(并非所有流都允许)和 2)在目标中预分配整个内存块一次以上是关于填充 MemoryStream 时出现 OutOfMemoryException:16GB 系统上的 256MB 分配的主要内容,如果未能解决你的问题,请参考以下文章
将 g 711 数据包转换为 wav 文件时出现断断续续的声音
填充数组时出现java.lang.NullPointerException [重复]