Monday, August 07, 2006

CLR processModel memoryLimit

People like Sam and Dustin probably like crawling around the CLR Internals and garbage collection (that is, the sewage system that keeps everything clean and running properly). For me, it's sometimes interesting, but mostly frusterating. I just want things to work without necessarily knowing why they are working.

Enter a case that my friend and co-worker (btw, you need a website/blog, Murph) has been trying to research and resolve for a month or two now.

The client uses Crystal Reports for web-based reporting. Despite my distaste for CR, this actually isn't the problem, and the reports work just fine for what they need to do. The problem is more related to the fact that web-based reporting needs to use a postback when paginating through the report. The reports are based on Datasets, which are retrieved from a web service (for security reasons). Therefore, in order to prevent querying the database every time the user goes to the next page, the Dataset is cached in the Session.

Well, some of these reports have huge amounts of data associated with them. It seems that if too many reports were requested since the last time that the server was bounced, that they would start to get OutOfMemory exceptions. This, in spite of 3.5GB of RAM on the server.

My first thought was to move away from In-Proc session management (i.e., try the SQL Server-based model). That still didn't work. It was as if garbage collection wasn't doing its job.

Murph then started messing with the setting in machine.config. By default, there's a memoryLimit="60" setting, which means that when the memory pressure of the ASP.NET worker process reaches a 60% threshhold, that it will start a new process (i.e., recycle itself, which by definition, gets rid of uncollected garbage and frees up physical memory).

This sounds all well and good. After all, I like when the system has a failsafe mechanism that cleans up after itself. But, in this case, there was 3.5GB of RAM:

3.5GB * 60% = 2.1GB. If memory usage hits 2.1 GB, then the ASP.NET worker process will recycle itself.

Only, it seems that by default, .NET only allows 2GB of memory for its processes. Therefore, before the 2.1GB threshhold was reached, they got the OutOfMemory exception.

The following was invaluable for helping to resolve the problem:

Source: Improving .NET Application Performance and Scalability - Chapter 17

Configure the Memory Limit
The memory threshold for ASP.NET is determined by the memoryLimit attribute on the element in Machine.config. For example:

<processModel ... memoryLimit="60" .../>

This value controls the percentage of physical memory that the process is allowed to consume. If the worker process exceeds this value, the worker process is recycled. The default value shown in the code represents 60 percent of the total physical memory installed in your server.

This setting is critical because it influences the cache scavenging mechanism for ASP.NET and virtual memory paging. For more information, see "Configure the Memory Limit" in Chapter 6, "Improving ASP.NET Performance." The default setting is optimized to minimize paging. If you observe high paging activity (by monitoring the Memory\Pages/sec performance counter) you can increase the default limit, provided that your system has sufficient physical memory.

The recommended approach for tuning is to measure the total memory consumed by the ASP.NET worker process by measuring the Process\Private Bytes (aspnet_wp) performance counter along with paging activity in System Monitor. If the counter indicates that the memory consumption is nearing the default limit set for the process, it might indicate inefficient cleanup in your application. If you have ensured that the memory is efficiently cleaned but you still need to increase the limit, you should do so only if you have sufficient physical memory.

This limit is important to adjust when your server has 4 GB or more of RAM. The 60 percent default memory limit means that the worker process is allocated 2.4 GB of RAM, which is larger than the default virtual address space for a process (2 GB). This disparity increases the likelihood of causing an OutOfMemoryException.

To avoid this situation on an IIS 5 Web server, you should set the limit to the smaller of 800 MB or 60 percent of physical RAM for .NET Framework 1.0.

/3GB Switch
.NET Framework 1.1 supports a virtual space of 3 GB. If you put a /3GB switch in boot.ini, you can safely use 1,800 MB as an upper bound for the memory limit.

You should use the /3GB switch with only the following operating systems:

Microsoft Windows Server™ 2003
Microsoft Windows 2000 Advanced Server
Microsoft Windows 2000 Datacenter Server
Microsoft Windows NT 4.0 Enterprise Server
You should not use the /3GB switch with the following operating systems:

Microsoft Windows 2000 Server
Microsoft Windows NT 4.0 Server
Windows 2000 Server and Windows NT 4.0 Server can only allocate 2 GB to user mode programs. If you use the /3GB switch with Windows 2000 Server or Windows NT 4.0 Server, you have 1 GB for kernel and 2 GB for user mode programs, so you lose 1 GB of address space.

For IIS 6 use the Maximum used memory (in megabytes) setting in the Internet Services Manager on the Recycling page to configure the maximum memory that the worker process is allowed to use. As Figure 17.12 shows, the value is in megabytes and is not a percentage of physical RAM.