"This whole article started because of the a memory leak in our Fares Engine.... It has become one of the longest debugs of my career."

OutOfMemoryException

The maximum allocatable size of a .NET process depends on the following factors:

  • The CPU, which for the .NET universe is
    • x86 which has a 4GB address range, although in practice the actual available memory is around 3.2GB as the BIOS and video share the available address range.
    • x64 which has an address range of 18.45 exabytes which is practically unlimited (although we said that about 386, x86 and y2k)
    • Italium , which is beyond the scope of this article.
  • The Windows operating system
    • Windows 98/ME
    • Windows NT/XP/2000 (x86 and x64)
    • Vista (x86 and x64)
    • Windows Server 2003 (x86 and x64)
  • The headers PE signature of the OS Process /LARGEADDRESSAWARE
  • Available swap space

The x64 platform (x64 CPU, x64 OS) will yield a maximum process size in excess of the available system RAM. For example, my 8GB Windows 7 machine was able to yield a process of 8,656 MB, although at that point the system was swapping so much it was unusable.

The x86 platform (x86/x64 CPU, x86 OS) will yield 1.5GB as the maximum process size be ##DEFAULT##. Most people assume this will be 2GB, however the garbage collector needs its own space and does not allocate the maximum.

It is possible to allocated over 2GB on a .NET x86 process by marking the assembly or EXE with /LARGEADDRESSAWARE. This cannot be done via Visual Studio, although you can create a Post Build Event (see attached solution for an example). Execute the following command-line tool EDITBIN.EXE /LARGEADDRESSAWARE MyApp.exe. This command can normally be found in C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin. In order to confirm that the binary headers have been correctly updated run the following command DUMPBIN.EXE /HEADERS MyApp.exe, which should give:

Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file MaxMemory-x86-LargeAddressAware.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
             14C machine (x86)
               3 number of sections
        49FDF23D time date stamp Sun May 03 20:36:29 2009
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             122 characteristics
                   Executable
                   Application can handle large (>2GB) addresses
                   32 bit word machine

Strangely, it easier to test this on an x64 system which has lots of available memory. In tests (see test utility described later) I have been able to allocate 2'803MB.

It is more complex on a x86 systems as the operating system must have the /3GB switch in the c:\boot.ini file. For example, marking an .NET application with /LARGEADDRESSAWARE, but running it on a Windows XP system will yield 1,500MB max -- even if the machine has the maximum physical RAM of 4GB/3.2GB free! You need to have both /LARGEADDRESSAWARE on the specific application, and the operating system configured with /3GB.

.NET Maximum Memory Test Tool for x86, x64

I have found it tricky to isolate the different factors and to marry up the theory with actual hands-on results. To this end I created a utility allocate increasing amounts of memory (in 64KB chunks) to determine the actual maximum allocatable memory for a particular system (CPU, OS, RAM, swap space, and configuration /3GB, /PAE)

Full source (GPL) and compiled .EXE are available for download from the footer of this article. No installation is required.

The utility comes in three flavours:

  • x64 -- called "MaxMemory-x64.exe"
  • x86 -- called "MaxMemory.exe"
  • x86 + /LARGEADDRESSAWARE -- called "MaxMemory-x86-LargeAddressAware.exe"

[MaxMemory.exe screenshot]
Fig. 004 -- MaxMemory.exe screenshot

Table of Findings

  • I am able to allocate 8,656MB using "MaxMemory-x64.exe" on x86, Windows 7, 8GM RAM
  • I am able to allocate 2,803MB using "MaxMemory-x86-LargeAddressAware.exe" on x86, Windows 7, 8GM RAM
  • I am able to allocate 1,411MB using "MaxMemory.exe" on x86, Windows 7, 8GM RAM
  • I am able to allocate 2,191MB using "MaxMemory-x86-LargeAddressAware.exe" on x86, Windows XP + /3GB, 2'496MB RAM, VMWare
  • I am able to allocate 2,178MB using "MaxMemory-x86-LargeAddressAware.exe" on Xeon, Windows Server 2003 SE + /3GB, 2GB RAM, VMWare

I am sure the tweaking the allocations and system configuration by yield better results, and that the actual number may also vary from attempt to attempt.

On the x86 platform, allocations above the 1,500MB mark seem to be very slow. I do not know why this is the case.

A very good article, showing various approaches to .NET Reliablity and related defensive techniques.

-- Comments and refinements are welcome, Author.