填充MemoryStream时OutOfMemoryException:在16GB系统上分配256MB

c00*_*0fd 18 c# memory asp.net out-of-memory

我在我的开发IIS服务器(来自VS2010 IDE)上运行以下方法,在64位Windows 7计算机上安装了16 GB的RAM:

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;
}
Run Code Online (Sandbox Code Playgroud)

我得到了System.OutOfMemoryException这一行:

ms.Write(buff, 0, nSz);
Run Code Online (Sandbox Code Playgroud)

分配268435456个字节时抛出:

Alloc size = 268435456

这是0x10000000或256 MB.所以我想知道是否需要设置一些全局设置才能使其正常工作?

以下是项目配置设置的屏幕截图: 在此输入图像描述

Lan*_*kin 28

简答 - 开发服务器是32位进程.

"为什么只有256Mb?"的答案很长

首先,让我们了解它是如何工作的.

MemoryStream有内部byte []缓冲区来保存所有数据.它无法预测此缓冲区的确切大小,因此它只是用一些初始值初始化它.

位置和长度属性不反映实际的缓冲区大小 - 它们是反映写入的字节数的逻辑值,并且很容易小于实际的物理缓冲区大小.

当这个内部缓冲区不能适合所有数据时,它应该"重新调整大小",但在现实生活中它意味着创建两倍于前一个缓冲区大小的新缓冲区,然后将数据从旧缓冲区复制到新缓冲区.

因此,如果缓冲区的长度为256Mb,并且您需要写入新数据,这意味着.Net需要找到另一个512Mb数据块 - 其余部分都已就绪,因此堆应该至少为768Mb收到OutOfMemory时的内存分配时刻.

另请注意,默认情况下.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;
        }
Run Code Online (Sandbox Code Playgroud)

如果它内置在x64/AnyCPU并从控制台运行 - 一切正常.

如果它是跨x86构建的 - 它在控制台中失败了.

如果你说它是内置x64的Page_Load,并从VS.Net web服务器打开 - 它就失败了.

如果你对IIS做同样的事情 - 一切都很好.

希望这可以帮助.

  • 最重要的是,分配的512 MB必须是连续的内存块.并不是说32位的地址空间不足; 只是地址空间是碎片化的.使用多个较小字节数组的Stream实现可以更接近2 GB限制. (10认同)

Ale*_*kov 6

如果您使用的是默认的VS开发服务器,那么您将在x86/32位进程中运行代码.如果您使用完整的IIS - 最有可能在IIS中,特定的AppPool配置为在x86(32位模式)下运行,因此地址空间非常有限(除非您将应用程序标记为大地址识别,否则为2GB).

如果是IIS,请确保已配置应用程序轮询以运行x64(不确定什么是默认值).确保您的代码目标设置为AnyCPU或x64.

对于独立的C#应用​​程序 - 默认情况下,它们使用x86或AnyCPU/Prefer x86进行编译 - 将目标平台更改为x64.

要获得对IIS的x64支持,您可以从下载IIS 8.0 Express 安装完整的IIS或安装IIS Express 8.0(Windows 7附带的7.5只是32位).

附注: