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

 

Published 20 December 05 09:19 by jamesche
Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Cheshire's Blog said on April 12, 2006 9:17 AM:
I may have said this before (my memory's not what it was prior to decade number 4), but the most common...
# Cheshire's Blog said on February 14, 2007 7:34 PM:

I may have said this before (my memory's not what it was prior to decade number 4), but the most common

# tribina said on July 25, 2007 11:45 AM:

I was wondering if you could expand on common causes of OOM when they are not the result of high memory conditions.  We have a web app that's experiencing this scenario.  The OS does not have the /3GB switch we have an app pool with 5 worker processes and debug is set to false.   The OOMs start to happen when any one of the 5 processes reaches a memory usage of 500M.  If the proc has passed that threshold we start getting OOMs.

Could you explain other reasons why this may happen?

Thank you.

# jamesche said on July 25, 2007 11:51 AM:

Tribina,

An OOM can happen because of memory fragmentation. When the CLR needs to grow the managed heap, it requires a large block of contiguous memory of up to 64MB in size. If there isn't a large enough contiguous block available, an OOM will occur.

There are many causes for a fragmented memory space. Your best bet is to open a case with PSS. We can get a dump of the process when the OOM occurs and debug it to see what's going on.

Jim

# tribina said on July 26, 2007 2:24 PM:

Thank you for replying.

How do you the processes dump?  I've used Debug Diagnostic Tool before.  Is this what you mean?  Also, when should the dumps be taken?  When start experiencing the OOMs?

I want to preemptively do this before placing a  call to PSS if I can.

Thanks.

# jamesche said on July 26, 2007 2:45 PM:

DebugDiag will work fine, but you'll need to set a Registry key and then set a breakpoint.

Add the following registry key.  This was added to the .NET Framework so that a breakpoint exception will be thrown when and OutOfMemory condition occurs.  This is documented in the following article:

820745 Failfast occurs when you experience an "out of memory" condition

http://support.microsoft.com/?id=820745

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\

DWord:  GCFailFastOnOOM

Value:  2

Configure DebugDiag to capture the memory dump when the BreakPoint Exception is thrown and when the process stops.

1.  Open DebugDiag

2.  On the Rules tab, click Add Rule

3.  Select Crash and click Next

4.  Select "All IIS Processes" and click Next

5.  Click Advanced Exception Configuration

6.  Click Add Exception

7.  Select Breakpoint Exception, change Action Type to Full UserDump

8.  Click OK

9. Click Save and Close

10. Click Advanced Breakpoint Configuration

11. Click Add Breakpoint

12. Type KERNEL32!ExitProcess and change Action Type to Full UserDump

13. Click OK

14. Click Save and Close

15. Click Next through the rest of the wizard

Inject LeakTrack.dll to capture native leak information:

1. Click the Processes tab

2. Right-click the ASPNET_WP.exe process (or w3wp.exe if running in IIS 6), select "Monitor for leaks"

The memory dump will automatically get captured when the OutOfMemoryException is thrown. You can then call PSS to open a case. Tell the support engineer that you get that I have told you to get a dump and they can contact me to debug it.

Jim

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

Search

This Blog

Syndication

Page view tracker