Freigeben über


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

Comments

  • Anonymous
    August 24, 2015
    Helpful info, thanks

  • Anonymous
    August 24, 2015
    @tomr : Happy to be of service ;)

  • Anonymous
    June 09, 2016
    One needs to point out here that size of virtual memory described here is "reserved" only and is not thought of as "used" by OS. This means that OS is not trying to allocate physical/page file space for this amount of memory at all. For example it is perfectly fine to have say 32 GB of physical memory + 32 GB page file (64 GB of total "real" ("commited" is the word) virtual memory possible on the system) AND say a sum of virtual memory reserved by IIS pools to be say 256 GB (say 16 pools each reserving 16 GB). This is because reserved memory page becomes "really used"("committed") only after application tries to write to it for the first time.Only committed pages are allocated real RAM and/or swapped to page file when memory is low.Learned this the "hard" way on a real server: I had 32GB memory+20 GB page file (52GB available for committed virtual memory) BUT at the same time SQL server reserved 33 GB and wp processes another 60+ GB. Everything kept working just fine, despite 93 GB memory reserved.Just my 2 cents :-)

  • Anonymous
    December 01, 2016
    It would be extremely useful to have this article include how these settings relates to a specific IIS Virtual Memory limit, and setting it to "0" (unlimited).

    • Anonymous
      January 03, 2017
      IIS (or rather the Windows Process Activation Service - or WAS - which is part of IIS), will have a look at the two parameters for a worker process when memory recycling is set up:- virtual memory: the amount of memory that is committed + the amount of memory that is reserved- private memory usage: the amount of memory that is committed (in use, either in RAM or in the paging file) for the worker process in question.You have to keep in mind that ASP.net is not the only consumer or memory in a process address space. Setting the 'virtual memory' recycling threshold lower than the minimum amount of reserved memory needed to the .Net CLR to load inside of your IIS worker process will cause the worker process to be recycled immediately. I have seen scenarios where w3wp.exe processes recycled all the time because of the threshold of virtual memory recycling that was too low.Paul
    • Anonymous
      May 04, 2017
      You should not use IIS memory recycling unless you think you have a memory leak. Setting the recycling thresholds lower than the memory that the .Net Framework will reserve when it initializes the heaps will immediately cause the IIS worker process to recycle. By the way, we have written an article on IIS memory and recycling thresholds - this was back in the day of IIS 6, but all the information inside is still valid for IIS 7 and above: https://blogs.msdn.microsoft.com/friis/2008/10/13/mmoire-recyclage-sous-iis-6/ - article is in French but you can use Bing translate to convert to English.