Why am I getting an Out Of Memory Exception in my C# application?

62,084

Solution 1

In a normal 32 bit windows app, the process only has 2GB of addressable memory. This is irrelevant to the amount of physical memory that is available.

So 2GB available but 1.5 is the max you can allocate. The key is that your code is not the only code running in the process. The other .5 GB is probably the CLR plus fragmentation in the process.

Update: in .Net 4.5 in 64 bit process you can have large arrays if gcAllowVeryLargeObjects setting is enabled:

On 64-bit platforms, enables arrays that are greater than 2 gigabytes (GB) in total size. The maximum number of elements in an array is UInt32.MaxValue.

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

Solution 2

Just additional to the other points; if you want access to a dirty amount of memory, consider x64 - but be aware that the maximum single object size is still 2GB. And because references are larger in x64, this means that you actually get a smaller maximum array/list size for reference-types. Of course, by the time you hit that limit you are probably doing things wrong anyway!

Other options:

  • use files
  • use a database

(obviously both has a performance difference compared to in-process memory)


Update: In versions of .NET prior to 4.5, the maximum object size is 2GB. From 4.5 onwards you can allocate larger objects if gcAllowVeryLargeObjects is enabled. Note that the limit for string is not affected, but "arrays" should cover "lists" too, since lists are backed by arrays.

Solution 3

Just to add to the previous replies: you can go beyond the 2Gb limit on systems booted with the /3Gb [and optionally userva] boot flags.

Solution 4

Check that you are building a 64-bit process, and not a 32-bit one, which is the default compilation mode of Visual Studio. To do this, right click on your project, Properties -> Build -> platform target : x64. As any 32-bit process, Visual Studio applications compiled in 32-bit have a virtual memory limit of 2GB.

Each process has its own virtual memory, called an address space, into which it maps the code that it executes and the data it manipulates. A 32-bit process uses 32-bit virtual memory address pointers, which creates an absolute upper limit of 4GB (2^32) for the amount of virtual memory that a 32-bit process can address. However, the operating system requires half of it (to reference its own code and data), creating a limit of 2GB for each process. If your 32-bit application tries to consume more than the entire 2GB of its address space, it will return “System.OutOfMemory”, even though the physical memory of your computer is not full.

64-bit processes do not have this limitation, as they use 64-bit pointers, so their theoretical maximum address space is 16 exabytes (2^64). In reality, Windows x64 limits the virtual memory of processes to 8TB. The solution to the memory limit problem is then to compile in 64-bit.

However, object’s size in Visual Studio is still limited to 2GB, by default. You will be able to create several arrays whose combined size will be greater than 2GB, but you cannot by default create arrays bigger than 2GB. Hopefully, if you still want to create arrays bigger than 2GB, you can do it by adding the following code to you app.config file:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

Solution 5

You've got a max of 2Gb addressable memory as a 32bit app, as the other posters mentioned. Dont forget about overhead. You're creating an array of 93 million objects - if there happens to be 4 bytes of overhead per object that's an extra 350Mb of memory.

Share:
62,084
George2
Author by

George2

Updated on March 12, 2020

Comments

  • George2
    George2 over 4 years

    My memory is 4G physical, but why I got out of memory exception even if I create just 1.5G memory object. Any ideas why? (I saw at the same time, in the performance tab of task manager the memory is not full occupied, and I could also type here -- so memory is not actually low, so I think I hit some other memory limitations)?

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace TestBigMemoryv1
    {
        class MemoryHolderFoo
        {
            static Random seed = new Random();
            public Int32 holder1;
            public Int32 holder2;
            public Int64 holder3;
    
            public MemoryHolderFoo()
            {
                // prevent from optimized out
                holder1 = (Int32)seed.NextDouble();
                holder2 = (Int32)seed.NextDouble();
                holder3 = (Int64)seed.NextDouble();
            }
        }
    
        class Program
        {
            static int MemoryThreshold = 1500; //M
            static void Main(string[] args)
            {
                int persize = 16;
                int number = MemoryThreshold * 1000 * 1000/ persize;
                MemoryHolderFoo[] pool = new MemoryHolderFoo[number];
                for (int i = 0; i < number; i++)
                {
                    pool[i] = new MemoryHolderFoo();
                    if (i % 10000 == 0)
                    {
                        Console.Write(".");
                    }
                }
    
                return;
            }
        }
    }
    
  • Robert C. Barth
    Robert C. Barth over 15 years
    The .5GB is can also be everything else running on the machine that isn't O/S components.
  • Nick
    Nick over 15 years
    No, each process gets a fully adressable 2 GB virtual memory space.
  • JaredPar
    JaredPar over 15 years
    Nick is correct. Each process's address space is independent of anothers. Unless they choose to dabble in shared memory.
  • uzbones
    uzbones over 15 years
    Although to use the /3Gb switch you will have to modify the executable to manually set a flag in it for it to be able to take advantage of the boot flag.
  • George2
    George2 over 15 years
    1. Hi JaredPar, 1.5G memory limitation is for per process or per thread? 2. Do you have any documents for this limitation? :-)
  • George2
    George2 over 15 years
    Hi uzbones, does it mean 4G memory is useless? My application could only consume maximum 2G?
  • JaredPar
    JaredPar over 15 years
    @George2 it's per process. Link msdn.microsoft.com/en-us/library/ms810627.aspx Read down to the section titled "Virtual memory in Win32"
  • George2
    George2 over 15 years
    Hi JaredPar, but how much we could use from user mode? I think we should be able to use more than 2G since Windows should use swap page file. Any comments?
  • Marc Gravell
    Marc Gravell over 15 years
    Swap file is irrelevant; you can only address 2GB... well, unless you use the 3GB switch on a server and mark the exe large address aware...
  • JaredPar
    JaredPar over 15 years
    When you say max single object are you talking about CLR objects or raw allocation size (native or managed). I assume the former but wanted to check. Also do you have a reference for this, I hadn't seen that yet. Can't imagine why you would want to exceed 2GB though for a single object.
  • George2
    George2 over 15 years
    1. "be aware that the maximum single object size is still 2GB" -- Marc, do you have documents to prove this statement? I am especially interested in what means single object, as we can composite objects to form new objects, so what means single object in your context?
  • George2
    George2 over 15 years
    1. How do you get the conclusion -- "this means that you actually get a smaller maximum array/list size for reference-types." from "because references are larger in x64"? Could you provide more details please?
  • uzbones
    uzbones over 15 years
    Well... no, by having more than 4G of memory, you could run two copies of you program each consuming 2G of memory each. And as KristoferA mentions further below there is a system switch that can be done to change the amount to 3G, or you need to go 64-bit.
  • George2
    George2 over 15 years
    I have made more study by myself. I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space, correct?
  • George2
    George2 over 15 years
    I have made more study by myself. I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space, correct?
  • George2
    George2 over 15 years
    I have made more study by myself. I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space, correct?
  • George2
    George2 over 15 years
    I have made more study by myself. I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space, correct?
  • George2
    George2 over 15 years
    I have made more study by myself. I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space, correct?
  • Marc Gravell
    Marc Gravell over 15 years
    Windows itself imposes the 2gb/3gb limit per process on win32. The theoretical limit with 32-bit references is 4gb. Win64 blows both of these limitations out of the water.
  • George2
    George2 over 15 years
    Thanks Marc! Is my understanding correct? -- "I the the reason why there is limitation in 32-bit system is because application access memory by using virtual address, even if we could have more than 4G physical memory, but the actual root limitaiton virtual memory address space"
  • Marc Gravell
    Marc Gravell over 15 years
    It depends whether by "virtual memory" you mean the virtualized memory model within a process, or whether you mean the "swap" data. It is it the first.
  • Marc Gravell
    Marc Gravell over 15 years
    msdn.microsoft.com/en-us/library/ms241064(VS.80).aspx "As with 32-bit Windows operating systems, there is a 2GB limit on the size of an object you can create while running a 64-bit managed application on a 64-bit Windows operating system.". The array itself counts as a single object.
  • Marc Gravell
    Marc Gravell over 15 years
    Note that in the case of ref-types (classes), the array only holds the references. In x86, a pointer/reference is 4 bytes; in x64 it is 8 bytes. Thus you get half as many references in the maximum array: informit.com/guides/content.aspx?g=dotnet&seqNum=659
  • Marc Gravell
    Marc Gravell over 15 years
    The other option is a jagged array; unlike a rectangular array, this composes an array of arrays (or list of lists, etc), giving you a much larger count of references, simply by not having a single huge object, but lots of fairly large objects.
  • uzbones
    uzbones over 15 years
    Yes, in a 32bit system in order to access more than 4G of memory (2G user-mode and 2G system) the operating system would need to use something bigger than a 32-bit int for an index. You can get around this by using AppDomains en.csharp-online.net/…
  • geofftnz
    geofftnz over 15 years
    Yeah, pretty much. All your pointers are stored in 4 bytes, which sets a limit of how much they can see. Things were even worse back in the days of 16-bit pointers. Dont ask me about segment:offset or windowing high memory...
  • George2
    George2 over 15 years
    I read "As with 32-bit Windows operating systems, there is a 2GB limit on the size of an object you can create while running a 64-bit managed application on a 64-bit Windows operating system." -- but very confused. It is 2G limit on 32-bit or on 64-bit?
  • Marc Gravell
    Marc Gravell over 15 years
    Any comments?: regardless, there is this limitation. I'm not sure that I can find a good reason to chanllenge this limitation; there are other ways (jagged etc) of getting bit sets of data without needint a single contiguous block.
  • Marc Gravell
    Marc Gravell over 15 years
    Re 2GB: both 32 and 64 impose a 2GB-per-single-object limitation. But you can have multiple arrays to have bigger sets of data.
  • Leon Breedt
    Leon Breedt over 14 years
    In addition, if you do interop with native code that does lots of small allocations, you may encounter OutOfMemoryException as well when you get back to .NET and the CLR tries to allocate a contiguous chunk (I believe LOH chunk size is between 16MB and 64MB, but I think that varies) to expand the heap. This is much less likely to occur on 64-bit though due to increased address space.
  • Alexei Levenkov
    Alexei Levenkov over 10 years
    I've added gcAllowVeryLargeObject comment as this is top accepted question (+1 to Marc Gravell answer too since it already contains this information).
  • ilasno
    ilasno about 9 years
    +1 for the Visual Studio build properties - this allowed my application to use a little over 4GB that it needed.