Tuesday, October 30, 2007

OutOfMemoryException: Diagnosis and Indications

If you are seeing System.OutOfMemoryException in your ASP.NET application logs, you are probably looking for a way to determine what is chewing up all of your RAM.

Static code analysis such as that performed by FXCop and the Developer SKU of Visual Studio Team System is only useful if you know what to look for and that what you are looking for is in your code. It is possible that you are accreting too much into the Session, but it is also possible that ASP.NET 2.0 has a memory leak or that the Telerik controls are doing something funky. Static code analysis cannot tell you that.

Performance monitoring tools cannot, generally, peek into your process space and say, "this session is using this much memory" or "this http handler is not releasing resources". These tools (e.g. perfmon) can only track the information supplied by the application's stack, i.e. IIS, ASP.NET, our code, etc. So, information like cache misses or worker process age is available, but the internals of the application are not. One way to work around this is to host the session in the state server, as the ASP.NET Session State Server provides a lot of visibility in the way of performance counters. This may or may not aid us depending on if the problem is indeed session state memory pressure.

Another tool that is not often mentioned and egregiously under-utilized by development and operations teams are profilers. If you are familar with SQL Server Profiler, then you already have some idea of how profilers work. The .NET platform itself comes with a free profiler from Microsoft referred to as the CLR Profiler. This can be used to gain completely visibility into any process that runs on the .NET framework. Whereas static code analysis forces you to pontificate on how the application might be, and the performance monitoring tools have limited visibility, the profiler tools gives you a complete picture as to how the process actually is--if you know how to use them.

K. Scott Allen—whose blog you most certainly have seen if you've been doing serious ASP.NET development—has an excellent article on getting CLR Profiler to work with Cassini (Visual Studio's built-in web server).

If you are not familiar with CLR Profiler, keep it in mind if you see this error in your own applications. It has some limitations. For example, it cannot be used to profile a running process, and it will decrease the performance of your application an awful lot. So it is used as a modeling tool rather than a forensic tool.

However, other approaches may be of more immediate use. The approach we chose to implement was to run the application in a web garden configuration. In a web garden configuration, you generally have the application running in its own application pool. The Session state must be kept out-of-process, i.e. using the ASP.NET Session State Server or SQL Server, as requests have no affinity to the worker process that created their Session. This approach may work especially well on a multi-core machine where additional processes can take advantage of the parallelism. We then proactively recycle worker processes in our application pool to reset memory usage.

In the end, given time and budget, identifying the culprit in your application with a profiler is the best solution. But, IIS and ASP.NET provide great tools to deal with this kind of intermittent problem.