views:

130

answers:

4

We have a .NET application which our customers consider too large for mass deployment and we would like to understand what contributes to our memory footprint and is it possible to do any better without completely abandoning .NET and wpf.

We are interested in improving both Total Size and the Private Working Set (pws). In this question I just want to look at pws. VMMap typically reports a pws of 105 mb. Of this 11mb is image, 31mb is heap, 52 mb is managed heap, 7 mb is private data and the rest is stack, page table etc.

The largest prize here is the managed heap. We can account for approx 8mb of the manged heap directly within our own code, i.e. objects and windows we create and manage. The rest is presumable .NET objects created by the elements of the framework that we use.

What we would like to do is identify what element of the framework account for what portion of this usage and potentially re architect our system to avoid their use where possible. Can anyone suggest how this investigation can be done?

Further clarification:

I have used a number of tools so far, including the excellent ANTS profilers and WinDbg with SOS, and they do allow me to see the objects in the managed heap, but of real interest here is not 'What?', but 'Why?' Ideally I would like to be able to say, "Well, there a 10mb of objects been created here because we use WCF. If we write our own native transport we could save 8mb of that with x quality risk and y development effort."

Doing a gcroot on 300,000+ objects is not possible.

+2  A: 

WinDbg might be a useful tool for you. It comes with the Debugging Tools for Windows.

Once your app is running, you can attach WinDbg and explore the managed heap. (Or you can take a memory dump and explore it offline). It will be able to very quickly tell you the object types consuming the largest amounts of memory.

First you will need to load the SOS extension which enables debugging of managed applications:

.loadby sos mscorwks

Then you can use !dumpheap to get heap information, the -stat switch gives overall heap info on which types are allocated:

!dumpheap -stat

The -type parameter gives specific information on allocated instances of the specified type:

!dumpheap -type System.String

There's a bunch of other commands you might find helpful like:

  • !gcroot - to follow an allocated object back up it's root to find why it is in memory.
  • !dumpobj - to dump out a specific object so you can see it's contents.
  • !EEHeap - to give some general heap stats.

MSDN has a full list of SOS commands and their switches.

WinDbg is a pretty complex tool, but there are lots of tutorials and guides online if you search to help you get started. Alternatively, I can recommend the book Debugging Microsoft .NET 2.0 Applications by John Robbins which goes into some good detail in the .net debugging abilities of WinDbg and SOS.

You can load the SOS extension into visual studio instead by entering this into the immediate window, then you should be able to use the SOS commands directly in the VS immediate window:

.load SOS.dll

You also might find the CLR Profiler and this Usage guide helpful.

Simon P Stevens
I have been using WinDbg, but I am having difficulty tying the objects in the heap to the framework elements that own them. Any advice on this?
Downward Facing God
A combination of !gcroot and !dumpobj usually does the job for me. If all your code is in a common namespace can't you just do something like !dumpheap -stat -type YourCompaniesNamespace. That will give you the biggest memory usage classes within your code and you can see if there is anything worth picking off. All your going to get from WinDbg is clues, you'll have to jump back into code to tie those clues up with some actual behaviour.
Simon P Stevens
Thanks, this gives me an idea of where to start from, I don't think there is too much gravy in our own namespace currently (we have been through an optimization cycle with already), but I can do this with the known references in our project. I'll try this out and add any additional details.
Downward Facing God
I have checked all of the external referenced assemblies (system.data, system.xml , etc.) by doing !dumpheap -stat -type system.xmlThis has given we a list of all the objects from that namespace. This isn't the whole picture as there are obviously general .net objects (mostly in the system namespace) which exist in support of the checked assemblies. It is a good start in what I am trying to do, especailly has assemblies that are barely used (small number of objects) may be good candidates for removal to save on image size (minimal work required to implement provided functionality)
Downward Facing God
@Downward: Are you talking about managed heap memory or loaded assemblies taking up memory. That's something very different. WinDbg is telling you managed heap memory usage, not the amount of space required to load assemblies like System.xml
Simon P Stevens
@Simon: Both :), but mainly trying to understand which assemblies (which are also loaded) create the objects on the managed heap, that I haven't directly created.
Downward Facing God
+1  A: 

CLR profiler also shows memory allocated by type in the heap graphically.

nitroxn
+1  A: 

Any decent memory profiler will show you this information. You don't really want to mess with the free CLR Profiler, it isn't worth your time, get a decent 3rd party tool. You'll find them mentioned in this thread.

Hans Passant
I have the ANTS profiler and I am using WinDbg, but tying the objects in the heap to specific parts of the framework is proving difficult.
Downward Facing God
I can't see your screen from here. Document your problem better, what objects of what classes do you see?
Hans Passant
A: 

I am using .NET Memory Profiler. This is also used by some Microsoft teams internally as told by a PDC podcast.

Kai Wang