The Infamous Debug Attribute

Our most common issues deal with memory problems. Memory problems come in many flavors, but one of the more common ones is the OutOfMemory exception or OOM.

A lot of the OOM issues we see are due to high memory conditions, but some are not. When an OOM condition is not accompanied by high memory usage, it can be confusing. Hopefully I can shed some light on that by discussing one of the most common causes of OOM in a lower-than-expected memory consumption scenario.

An OOM occurs when a memory allocation fails for some reason. In an ASP.NET application, an OOM is often the result of the CLR not being able to allocate a new segment for the managed heap. These allocations are 32MB for lower generations and 64MB for the LOH, and the memory for the segment must be contiguous. Therefore, you may have 400MB of free memory for the process to use, but if we can't get a large enough contiguous block, you'll see an OOM because of memory fragmentation.

So what causes fragmentation? In our world, one of the most common causes is having a large number of dynamic assemblies loaded into the process, and one of the most common causes of that is the infamous debug attribute in the web.config file.

The debug attribute looks like this:

<compilation debug="true" />

In ASP.NET 1.x, the debug attribute is true by default. When the debug attribute is true, ASP.NET batching is disabled and each page gets dynamically compiled into its own assembly (DLL file) and loaded into the app domain. While each of these dynamic assemblies is small in size, we don't load them into any particular address space so they end up getting scattered thoughout memory. If there are enough of them, you can end up being so badly fragmented that OOM conditions occur. However, we've seen a lot of other problems caused by having debug set to true as well. Rule of thumb; in a production environment, the debug attribute should always be set to false.

So what is this batch compile thing?

When you browse an ASP.NET application, your application executes from a dynamic assembly that is copied into the Temporary ASP.NET Files directory on the Web server. That assembly gets loaded into your application domain as well. When batch compile is enabled (it is by default unless debug is true), ASP.NET will compile all pages in a particular directory into a single DLL. When batch compile is disabled (and it will be if debug is true), each page is compiled into a separate assembly. (In a future blog, I'll go into how you can tell if an assembly it batched.) That means that if you have 1,000 pages, you'll have 1,000 assemblies just for your pages. That doesn't take into account other dynamic assemblies that are created due to XML serialization and for other reasons.

What do I lose by setting debug to false?

I talk to a lot of developers who resist setting debug to false because they tell me that they want to get stack trace information in their exception logging. Good news! The debug attribute has nothing at all to do with getting stack trace information. The purpose of the debug attribute is to ensure that a particular page gets compiled into its own DLL so that you can debug it in Visual Studio, etc. Debug can be set to false and it will have no effect on your ability to get stack trace information and to do post-mortem debugging with Windbg.

As a side-note, you can get managed stack information without symbols as well. Symbols are required for source debugging and for seeing source file names and line numbers, but stack information in a managed application is obtained by other methods.

Okay, if I go on much longer, you'll lose patience and stop reading (if you haven't already), so I'll stop here for now.

Jim