web-dev-qa-db-ja.com

MemoryStreamの生成中のOutOfMemoryException:16GBシステムで256MBの割り当て

私の開発IISサーバー(VS2010 IDEから))で、16GBのRAMがインストールされた64ビットWindows 7マシンで次のメソッドを実行しています:

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バイトが割り当てられるとスローされます。

Alloc size = 268435456

これは0x10000000または256 MBです。それで、それを機能させるために設定する必要があるグローバル設定があるかどうか疑問に思っていますか?

これがプロジェクトの構成設定のスクリーンショットです。 enter image description here

18
c00000fd

短い答え-開発サーバーは32ビットプロセスです。

「なぜ256Mbなのか」に対する長い回答

まず、それがどのように機能するかを理解しましょう。

MemoryStreamには、すべてのデータを保持するための内部byte []バッファーがあります。このバッファーの正確なサイズを予測することはできないため、初期値で初期化するだけです。

PositionプロパティとLengthプロパティは実際のバッファーサイズを反映していません。これらは書き込まれたバイト数を反映する論理値であり、実際の物理バッファーサイズよりも簡単に小さくなります。

この内部バッファーがすべてのデータに収まらない場合は、「サイズ変更」する必要がありますが、実際には新しいバッファーの作成前のサイズの2倍のサイズで、古いバッファーからデータをコピーします新しいバッファに。

したがって、バッファの長さが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;
        }

X64/AnyCPUを組み込み、コンソールから実行する場合-すべて問題ありません。

X86でビルドした場合-コンソールで失敗します。

X64に組み込まれているPage_Loadと言って、VS.Net Webサーバーから開くと、失敗します。

IISで同じことをする場合-すべてが大丈夫です。

お役に立てれば。

27
Lanorkin

デフォルトのVS開発サーバーを使用している場合は、x86/32ビットプロセスでコードを実行しています。フルを使用している場合IIS-ほとんどの場合IIS特定のAppPoolはx86(32ビットモード)で実行するように構成されているため、アドレス空間が非常に限られています。 (アプリケーションをLarge Address Awareとしてマークしていない限り2GB)。

IISの場合は、アプリポーリングがx64を実行するように構成されていることを確認してください(デフォルトが不明)。コードのターゲットがAnyCPUまたはx64に設定されていることを確認してください。

スタンドアロンC#アプリケーションの場合-デフォルトではx86またはAnyCPU/Prefer x86でコンパイルされます-ターゲットプラットフォームをx64に変更します。

IISのx64サポートを取得するには、 フルIISをインストール またはインストールIIS Express 8.0(Windows 7に付属する7.5は32ビットのみ) Download IIS 8.0 Express から).

サイドノート:

6
Alexei Levenkov