How to Troubleshoot Out of Memory Issues (System.OutOfMemoryException) in ASP.NET

Article ID: 2020006 - View products that this article applies to.
Expand all | Collapse all

Symptoms

One of the most common issues that we see in Microsoft Customer Support Services is OutOfMemoryException scenarios. Therefore, we’ve put together a collection of resources to assist in troubleshooting and identifying the cause of memory issues.

Before we cover the details of troubleshooting an OutOfMemoryException, it’s important to understand what causes this problem. Contrary to what many developers believe, the amount of RAM that is installed does not impact the possibility of an OutOfMemoryException. A 32-bit operating system can address 4GB of virtual address space, regardless of the amount of physical memory that is installed in the box. Out of that, 2GB is reserved for the operating system (Kernel-mode memory) and 2GB is allocated to user-mode processes. The 2GB allocated for Kernel-mode memory is shared among all processes, but each process gets its own 2GB of user-mode address space. (This all assumes that you are not running with the /3gb switch enabled.)

When an application needs to use memory, it reserves a chunk of the virtual address space and then commits memory from that chunk. This is exactly what the .NET Framework's garbage collector (GC) does when it needs memory to grow the managed heaps. When the GC needs a new segment for the small object heap (where objects smaller than 85K reside), it makes an allocation of 64MB. When it needs a new segment for the large object heap, it makes an allocation of 16MB. These large allocations must be satisfied from contiguous blocks of the 2GB of address space that the process has to work with. If the operating system is unable to satisfy the GC's request for a contiguous block of memory, a System.OutOfMemoryException (OOM) occurs.

Note: A 32-bit process running on a 64-bit OS can address 4GB of user-mode memory, and a 64-bit process running on a 64-bit OS can address 8TB of user-mode memory, so an OOM on a 64-bit OS isn’t likely. It is possible to experience an OOM in a 32-bit process running on a 64-bit OS, but it usually doesn’t occur until the process is using close to 3GB of private bytes.

There are two reasons why you might see an OOM condition.

1. Your process is using a lot of memory (typically over 800MB in a 32-bit environment.)

2. The virtual address space is fragmented, reducing the likelihood that a large, contiguous allocation will succeed.

It's also possible to see an OOM condition due to a combination of 1 and 2.

(See http://blogs.msdn.com/b/webtopics/archive/2009/05/22/troubleshooting-system.outofmemoryexceptions-in-asp.net.aspx for more information.)

When an OOM occurs, you may notice one or more of the following symptoms.

  • Your application crashes.

o   For more information, see this blog post.

  • Your application may experience high memory as indicated by Task Manager or Performance Monitor.

o   For more information on troubleshooting high memory, see Tom Christian’s blog or Tess Ferrandez’s blog, or the KB article How to use the IIS Debug Diagnostics tool to troubleshoot a memory leak in an IIS process

  • Requests may take a long time to process.

o   On IIS 7, you can use Failed Request Tracing (FREB) to troubleshoot long-running requests.

  • Users may report an error message in the application due to the OOM.

Cause

When it comes to determining the cause for an OOM condition, you are actually working to determine the cause for either a high memory situation or a fragmented address space. While there is no way to document all of the possible causes of these situations, there are some common causes that we see regularly.

The following information outlines common causes of OOM conditions. For information on resolving each of these causes, see the Resolution section of this article.

String Concatenation

Strings in a managed application (an application written using the .NET Framework) are immutable. When a new value is assigned to a string, a copy is made of the existing string and the new value is assigned to the new string. This doesn’t typically cause any problems, but when a large number of strings are concatenated, it ends up causing many more string allocations than a developer might realize, and this can lead to memory growth and OOM conditions.

See this Knowledge Base article for more information.

Fragmentation in the Managed Heap

The garbage collector (GC) in a managed application compacts the heaps to reduce the amount of fragmentation. However, it’s possible to pin objects in a managed application, and pinned objects cannot be moved during heap compaction because doing so would change the address at which the object is located. If an application pins a large number of objects and/or pins objects for a long time, that can cause fragmentation in the managed heap. That can lead to the GC growing the managed heap more often and causing an OOM condition.

See this blog post for more information.

Fragmentation in the Virtual Address (VA) Space

As discussed previously, each process has a certain amount of memory allocated to it, and that memory represents the VA space for the process. If the VA space becomes fragmented, it increases the likelihood that the GC will not be able to obtain a large block of contiguous memory to grow the managed heaps, and that can lead to an OOM condition.

Fragmentation in the VA space is often caused by one or more of the following scenarios.

  • Loading the same assemblies into multiple application domains.
  • Running an application in production with the debug attribute of the <compilation> element set to true. (See this blog post for more information.)
  • Use of scripting within XSL transforms or creating XmlSerializers. (See this blog post for more information.)

Returning Large Sets of Data

When using data from a database or other data source, it’s important to limit the amount of data returned. For example, caching the result of a query that returns an entire database table in order to avoid the cost of retrieving parts of data from the database when needed is not a good approach. Doing so can easily cause high memory and lead to an OOM condition. Allowing a user to initiate a similar query (e.g. return all employees in a company or all customers in the state of Texas with a last name starting with the letter S) is another common way to create a high memory situation.

Running in a Production Environment with Tracing Enabled

ASP.NET tracing is a powerful feature for troubleshooting applications, but it should never be left on in a production environment. ASP.NET tracing uses data structures such as DataTables to store trace information, and over time, these can cause a high memory condition that can lead to OOM.

See this blog post for more information.

Leaking Native Resources

Many managed resources will also make use of native resources. Because the GC doesn’t clean up native resources, a developer is responsible for implementing (and calling) the Dispose method in order to clean up native resources. If you are using a type that implements the IDisposable interface and you don’t call the Dispose method, you risk leaking native resources and causing an OOM condition.

See this blog post for more information.

 

    Resolution

    String Concatenation

    To avoid OOM due to string concatenation, make sure that you are using the StringBuilder class. For more information, see this article in the Microsoft Knowledge Base.

    Fragmentation in the Managed Heap

    We've worked on minimizing OOM conditions due to pinning since the .NET Framework 1.0. Incremental improvements have been made in each version. However, there are still design patterns you can implement that will be beneficial if you have a need to pin objects.

    For more information, see this blog post.

    Fragmentation in the Virtual Address (VA) Space

    As previously mentioned, fragmentation in the VA space is most often caused by a large number of assemblies loaded into the process. There are three common causes of this that we see.

    • Assemblies loaded into multiple application domains.
      • If you need to use an assembly in more than one application running in the same application pool, strong-name the assembly and install it into the GAC. By doing that, you ensure that the assembly is only loaded into the process one time.
    • Running an application in debug mode.
      • The debug attribute of the <compilation> element should be false when in production.
      • You can use the <deploy retail="true" /> configuration to ensure that debug is always disabled in product. See this page for more information.
    • Dynamic assemblies caused by XSLT scripting or XmlSerializers.

    Returning Large Sets of Data

    Always limit the amount of data that can be returned from a database. Don't allow queries such as "SELECT * FROM. . ." because you then have no control over how much data is displayed in your page.

    It's equally important to ensure that you're not displaying a large data result in UI elements such as the GridView control. In addition to the memory required for the returned data, you'll also be consuming large amounts of data in strings and in UI elements required to render the results. By implementing paging and validating input so that large sets of data aren't returned, you can avoid this problem.

    Running in a Production Environment with Tracing Enabled

    Tracing should be disabled in a production environment. You can do so by setting the "enabled" attribute of the <trace> element to false in your web.config file. Enabling retail deployment by using <deploy retail="true" /> also disables tracing in your applications.

    Leaking Native Resources

    Many managed objects also allocate native resources. These objects should implement the iDisposable interface and you should call the Dispose method on these objects when you no longer need them.

    For information about the Dispose pattern, see this page. For information on finalization and when you should use a finalizer, see this blog post.

    More Information

    Note This is a "FAST PUBLISH" article created directly from within the Microsoft support organization. The information contained herein is provided as-is in response to emerging issues. As a result of the speed in making it available, the materials may include typographical errors and may be revised at any time without notice. See Terms of Use for other considerations.

    Properties

    Article ID: 2020006 - Last Review: June 22, 2011 - Revision: 8.0
    APPLIES TO
    • Microsoft ASP.NET 2.0
    • Microsoft ASP.NET 1.0
    • Microsoft ASP.NET 1.1
    • Microsoft ASP.NET 3.5
    • Microsoft ASP.NET 4
    Keywords: 
    KB2020006

    Give Feedback

     

    Contact us for more help

    Contact us for more help
    Connect with Answer Desk for expert help.
    Get more support from smallbusiness.support.microsoft.com