Debugging Out of Memory Errors
So you've got this long running application that gradually takes over all the memory on the computer that it's running on. You've got a resource leak somewhere; but how do you track it down?
Task Manager should be your first stop in determining if your application has a problem with leaking memory. After you've started Task Manager and switched to the Processes tab, go to the View | Select columns menu choice. The dialog box that pops up gives you a list of columns you can add to the display, seven of which have labels beginning with "Memory." The first five (all but the two containing the word "Paged") can provide useful insight to what your application is doing with memory. If you see, for instance, a process that has its Peak Working Set constantly increasing, it's a clue that the process is in trouble.
To get a handle on where your problems really are, there's an even better tool. To run it, right-click on Computer in the Start menu and select Manage. Drill down in the TreeView on the left to (in Vista) Reliability and Performance Monitor or (in Windows 7) Performance Monitor and open it to start tracking key data about your computer.
As with Task Manager, after bringing up the monitor, you'll want to add the counters that will help you find your problem. To see what's available (and there are a lot of counters available), right-click on the monitor's graph and select Add counters. At the top of the list of counters are some .NET CLR counters that can be very helpful.
Under .NET CLR Memory, you'll find counters that track the memory used by Generation 0 objects (objects that haven't been around very long), Generation 1 objects (objects that survived one garbage collection because they were in use), and Generation 2 objects (objects that have survived more than one garbage collection).
Most applications have many Generation 0 or Generation 1 objects and fewer Generation 2 objects (most .NET objects have short lifespans). If you have a large amount of memory in the Generation 2 memory pool, it may indicate that either (a) objects that should be short-lived are hanging around longer than they should or (b) that more objects are being created than you expected. If the counter showing induced garbage collections is high, I'd look at whatever process is creating objects first. Garbage collection is triggered when the instantiation of a new object causes the memory budgeted for a generation to be exceeded and the induced garbage collections counter can tell you if that's happening too often.
Another set of counters tracks the Large Object Heap (LOH), which holds objects that need 85K of storage or more. If you know that you shouldn't have any objects in the LOH or, if the amount of storage seems out of proportion to the number of large objects you expected, you can focus on those objects in your application that require a significant amount of storage.
There's more. Expanding the .NET CLR Data section, for instance, reveals a set of counters that let you track how your connection pools are being used. Good debugging is driven by real data, and the monitor is a good place to get that data.
Posted by Peter Vogel on 08/04/2011 at 1:16 PM