!dumpheap –stat explained… (debugging .net leaks)

The most powerful command when debugging a managed memory leak is by far !dumpheap. It will show you all the objects on the managed heaps and using the different switches of !dumpheap you can display the output in virtually any way you want.

!dumpheap is a function of the sos.dll extension that comes with the framework installation (in the framework directory) and if you have the SDK installed you can find some basic help for it’s usage in C:Program FilesMicrosoft Visual Studio .NET 2003SDKv1.1Tool Developers GuideSamplessos.

There are two categories of objects stored on the heap

Objects that are rooted somewhere, i.e. something in the application has a pointer to them

Objects that have been created or un-rooted since the last garbage collectionA good trick if you want to know where you leak and want to avoid looking through a lot of data that will be garbage collected soon is to run a stress-test, then induce a GC by calling GC.Collect(3), take a memory dump, then stress it a bit more, induce a GC again and take another memory dump and compare the objects on the heap. The –stat switch (statistics) shows a summary, per type of the objects on the heap

The 1st column shows the method table for the type. If you dump out the actual data for an object you will find that the first DWORD is the method table and this contains links to information about the type, such as what its member variables are, what methods it implements etc. Essentially, with the method table you can uniquely identify the type.

The 2nd column lists the number of objects of this type on the heap, so in the example above we have 5,286,303 strings on the heap.

The 3rd column shows the total size of all the objects, so again, in the example above our 5,286,303 strings occupy a total of ~695 MB

However, total size is a truth with modification, what is shown in the 3rd column is the total size of the structures of the objects, not including member variables.

Lets take a System.Data.DataSet for example

0x060bbd2c22117,680 System.Data.DataSet

Here we can see that 221 datasets take up a total of 17,680 bytes, which means that on average they take up 80 bytes each. That would be a very, very small dataset if the size shown was the complete dataset.

If we dump out the contents of a dataset on the heap by first running !dumpheap –mt <methodtable> and then picking one out in the resulting list and running !dumpobj <address> on it we will see something like the following.

Technically there is no gen -1 but I believe the version of SOS that I was using printed -1 for the generation if there was no actual generation, i.e. if it was on the LOH or if it had already been freed.