OutOfMemoryException while populating MemoryStream: 256MB allocation on 16GB system

Solution 1:

Short answer - dev server is 32bit process.

Long answer for "why just 256Mb?"

First of all, let's understand how it works.

MemoryStream has internal byte[] buffer to keep all the data. It cannot predict exact size of this buffer, so it just initializes it with some initial value.

Position and Length properties don't reflect actual buffer size - they are logical values to reflect how many bytes is written, and easily may be smaller than actual physical buffer size.

When this internal buffer can not fit all the data, it should be "re-sized", but in real life it means creating new buffer twice as size as previous one, and then copying data from old buffer to new buffer.

So, if the length of your buffer is 256Mb, and you need new data to be written, this means that .Net need to find yet another 512Mb block of data - having all the rest in place, so heap should be at least 768Mb on the moment of memory allocation when you receive OutOfMemory.

Also please notice that by default no single object, including arrays, in .Net can take more than 2Gb in size.

Ok, so here is the sample piece which simulates what's happening:

        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;
        }

If it built in x64/AnyCPU and runs from console - everything is ok.

If it built across x86 - it fails in console.

If you put it to say Page_Load, built in x64, and open from VS.Net web server - it fails.

If you do the same with IIS - everything is ok.

Hope this helps.

Solution 2:

If you are using default VS development server you are running code in x86/32 bit process. If you are using full IIS - most likely in IIS particular AppPool is configured to runs in x86 (32 bit mode) and as result have very limited address space (2GB unless you have marked your application as Large Address Aware).

In case of IIS make sure you have configured app polls to run x64 (not sure what is default). Make sure your code is target is set to AnyCPU or x64.

For standalone C# applications - by default they are compiled with x86 or AnyCPU/Prefer x86 - change target platform to x64.

To get x64 support for IIS you can either install full IIS or install IIS Express 8.0 ( 7.5 that comes with Windows 7 is 32 bit only) from Download IIS 8.0 Express.

Side notes:

  • If you just installed full IIS make sure to update your solution to use IIS for the site's host.
  • I don't have machine to check if any additional steps need to use x64 support for IIS Express. Check out this question - Can't get IIS Express 8 beta to run website as 64-bit process - as it may give you some ideas.
  • You may also try to build your own x64 version of development server based on suggestions here - Is Visual Studio 2010 WebDev WebServer (Cassini) 64-bit compatible?