31

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;
        }
    }
}
Eddie
  • 53,828
  • 22
  • 125
  • 145
George2
  • 44,761
  • 110
  • 317
  • 455

7 Answers7

41

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>
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • The .5GB is can also be everything else running on the machine that isn't O/S components. – Robert C. Barth Feb 28 '09 at 04:52
  • 5
    No, each process gets a fully adressable 2 GB virtual memory space. – Nick Feb 28 '09 at 04:53
  • Nick is correct. Each process's address space is independent of anothers. Unless they choose to dabble in shared memory. – JaredPar Feb 28 '09 at 05:00
  • 1. Hi JaredPar, 1.5G memory limitation is for per process or per thread? 2. Do you have any documents for this limitation? :-) – George2 Feb 28 '09 at 06:14
  • @George2 it's per process. Link http://msdn.microsoft.com/en-us/library/ms810627.aspx Read down to the section titled "Virtual memory in Win32" – JaredPar Feb 28 '09 at 06:17
  • 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? – George2 Feb 28 '09 at 06:26
  • 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... – Marc Gravell Feb 28 '09 at 06:32
  • 1
    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 Mar 01 '09 at 09:02
  • 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. – Leon Breedt Apr 03 '10 at 00:23
  • I've added `gcAllowVeryLargeObject` comment as this is top accepted question (+1 to Marc Gravell answer too since it already contains this information). – Alexei Levenkov Mar 12 '14 at 01:47
10

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    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. – JaredPar Feb 28 '09 at 07:13
  • 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 Feb 28 '09 at 09:46
  • 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? – George2 Feb 28 '09 at 09:47
  • 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 Mar 01 '09 at 09:01
  • 1
    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. – Marc Gravell Mar 01 '09 at 13:04
  • 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" – George2 Mar 01 '09 at 13:11
  • 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 Mar 01 '09 at 14:05
  • http://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 Mar 01 '09 at 14:21
  • 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: http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=659 – Marc Gravell Mar 01 '09 at 14:23
  • 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. – Marc Gravell Mar 01 '09 at 14:25
  • Hi Marc, I read the document about "Array Size Limitations" you recommended. But I disagree it comments "In the 64-bit runtime, pointers are 8 bytes, which cuts the maximum size of your array in half, to 268,435,447 items." -- I think on 64-bit there should be no such limitaiton? Any comments? – George2 Mar 03 '09 at 09:37
  • 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? – George2 Mar 03 '09 at 09:41
  • 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 Mar 03 '09 at 16:15
  • 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. – Marc Gravell Mar 03 '09 at 16:16
5

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>
  • +1 for the Visual Studio build properties - this allowed my application to use a little over 4GB that it needed. – ilasno Apr 06 '15 at 18:59
5

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.

KristoferA
  • 12,287
  • 1
  • 40
  • 62
  • 1
    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. – uzbones Feb 28 '09 at 05:23
  • 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 Mar 01 '09 at 09:02
4

One more thing to be aware of; some .NET objects require 'contiguous' memory. i.e if you are trying to allocate a large array the system may need not only sufficient free memory in your process but also for all that free memory to be in one big chunk... and unfortunately the process memory gets fragmented over time so this may not be available.

Some objects/data types have this requirement and some don't... I can't remember which ones do, but I seem to recall StringBuilder and MemoryStream have different requirements.

Yort
  • 787
  • 8
  • 22
4

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.

geofftnz
  • 9,954
  • 2
  • 42
  • 50
  • 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 Mar 01 '09 at 09:04
  • 1
    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... – geofftnz Mar 02 '09 at 00:29
3

On a 32-bit Windows Operating system the maximum 'user-mode' memory that a single application can access is 2GB... assuming that you have 4GB of memory on the box.

Unmanaged VC++ Application's memory consumption on windows server

http://blogs.technet.com/markrussinovich/archive/2008/07/21/3092070.aspx

(It's funny you asked this because I asked almost the same thing yesterday...)

Community
  • 1
  • 1
uzbones
  • 1,422
  • 11
  • 15
  • Hi uzbones, does it mean 4G memory is useless? My application could only consume maximum 2G? – George2 Feb 28 '09 at 06:16
  • 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. – uzbones Feb 28 '09 at 23:10
  • 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 Mar 01 '09 at 09:00
  • 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 http://en.csharp-online.net/.NET_Architecture%E2%80%94Application_domains – uzbones Mar 01 '09 at 17:11