ASP.net segment heap sizes – or how much virtual memory my web-app will need

Many a times, customers come to me saying they have a feeling that their ASP.net application takes up more memory then it did before, especially if they are migrating from the .Net 2.0 Runtime to the .Net 4.0 Runtime and from a 32 bit architecture to a 64 bit architecture. Some time ago, I wrote a small cheat on .Net segment size vs Architecture, which you can find listed here:

    https://linqto.me/n/AspNetMemory

Today, I would like to go into a little more detail on how we go about computing the memory needed at startup by an ASP.net architecture, based on the machine we are running on, since there are several factors that come into play when calculating this sum.

Heaps, Heaps and more Heaps

.Net stores most of the variables you will create (except for the value types) on a data-structure called the heap. This lives in the process address space and grows as more and more variables are needed and allocated by the application. The key is the 'growing when needed'. If the .Net Framework simply waits to execute an instruction calling 'new' to allocate a variable, this would be very bad for performance reasons we will not discuss here. Hence the heap pre-allocates entire regions of memory (called segments) which can then be used to store variables.

The .Net Managed Heap is actually two data structures: the Small Object Heap and the Large Object Heap. The Small Object Heap (SOH) is used to store smaller sized objects. Everything that is larger in size than 86 Kb is placed on the Large Object Heap. You can learn more about the two by reading this article on my friend Tess's blog:

    https://blogs.msdn.com/b/tess/archive/2006/06/22/643309.aspx

Suffice to say, that when each of the two heaps is initialized, just before you application is loaded into the w3wp.exe process, a heap segment will be reserved for the SOH and a second heap segment will be reserved for the LOH. Hence we wind up with to heap segments of process address space that is reserved from the get go. To understand more about process address spaces and reserved memory, please go through the article I wrote together with my colleague Sylvain on memory management in a Windows process, some time ago:

    https://blogs.msdn.com/b/friis/archive/2008/10/13/m-moire-recyclage-sous-iis-6.aspx

What's inside the box

Your computer / server that is running the ASP.net application you have just written, be it a virtual machine or a physical machine, will be equipped with a CPU. The central processing unit can (and normally does) have more than one core. For modern processors, they tend to have multiple cores, multiple processors on the same chip. Each of the cores may be hyper threaded, resulting in the fact that Windows may see double the number of processors if each core is hyperthreaded.

If you start the Windows Task Manager, you can see how many cores you have available by looking at the Performance tab, on the CPU resources. If you only have one graph, make sure that you have selected the option (from the context menu) to Show the Logical Processors (see screenshot).

So why is this important? Because the .Net Framework will try and take maximum advantage of the architecture of the server / machine it is running on, and will make use of each logical core available. How can it do this? One way is by creating multiple Managed Heaps instead of just one. In this way, the memory allocation operations that are needed can be performed by the processor the heap is allocated to. Hence, you will have as many .Net Heaps (a SOH and LOH) as you have processors.

For the example screenshot above, the machine has eight processor cores. If we fire up an ASP.net application, the .Net Runtime will create 8 SOH and LOH heaps, each of which will reserve an initial segment of memory.

Don't forget about the architecture

The architecture that your computer runs is also a factor in the equation. Older servers used to run on 32 bit architectures, meaning that each pointer (number that points to an address in the process address space) had 32 digits which could be either 1 or 0. More recent machines have 64 bit architectures, meaning the pointers are 64 digits log.

The 64 bit architecture pointers are twice the size of the 32 bit ones, and hence we can represent a whole lot more virtual process address space on such an architecture. The .Net Framework can operate both on 32 and 64 bit architectures, but will create bigger or smaller heap segments base on the architecture it is running on.

Putting it all together

To answer the question: how much memory is reserved by the .Net Framework at the start of my ASP.net application, we need to take into consideration the factors listed above:

  • Each managed heap is actually composed of two heaps: SOH and LOH
  • There will be as many heaps as there are logical processors on the machine
  • Heap segment size depends on machine architecture.

With this in mind, we can now look at the .Net segment sizes based on architecture, Runtime version and heap type:

  • ASP.NET 2.0 on x86 : 64 Mb for small object segment per processor and 32 Mb for large object segment per processor
  • ASP.NET 2.0 on x64 : 512 Mb for small object segment per processor and 128 Mb for large object segment per processor
  • ASP.NET 4.x on x86 : 64 Mb for small object segment per processor and 32 Mb for large object segment per processor
  • ASP.NET 4.x on x64 : 1024 Mb for small object segment per processor and 256 Mb for large object segment per processor

[2nd of February 2016] Here is a small side note add on:

If you are running on a 32 bit architecture, based on the number of processors, the segment size will shrink as such:
- if you are running on a machine with more than 4 logical processors, the segment sizes for the managed heaps will be: 32 Mb for the small object heap and 16 Mb for the large object heap
- if you are running on a machine with more than 8 logical processors, the sizes are as follows: 16 Mb for the small object heap and 8 Mb for the large object heap

This is done to prevent the .Net Runtime from actually reserving more memory than is possible in a 32 bit address space (2 Gb max assuming that you are not using the /3GB swicth) 

[15th of February 2016] - and just for completeness, here is a table with all segment sizes and possibilities for the initial segment size:

Framework Version

Architecture

# of Logical Processors

Small Object Heap

Large Object Heap

Total par processor

.Net Framework 2

X86

Nb proc <= 4

64 Mb

32 Mb

96 Mb

.Net Framework 2

X86

4 < Nb proc <= 8

32 Mb

16 Mb

48 Mb

.Net Framework 2

X86

8 < Nb proc

16 Mb

8 Mb

24 Mb

.Net Framework 2

X64

Any

512 Mb

128 Mb

640 Mb

.Net Framework 4.x

X86

Nb proc <= 4

64 Mb

32 Mb

96 Mb

.Net Framework 4.x

X86

8 < Nb proc <=8

32 Mb

16 Mb

48 Mb

.Net Framework 4.x

X86

8 < Nb proc

16 Mb

8 Mb

24 Mb

.Net Framework 4.x

X64

Any

1024 Mb

256 Mb

1280 Mb