Tool for generating DGML graphs showing why your object can’t be collected (VisualGCRoot)

2 minute read

Visual Studio.NET 2010 has a new feature that allows you to create nice directed graphs with a markup language called DGML (Directed Graph Markup Language).

Visualizing object graphs with DGML

Chris Lovett wrote a tool that takes GCRoot output and transforms it to a DGML document so that you can see the roots of an object in a more visual way, and finally Mohamed Mahmoud went one step further and made a debugger extension that does this for you directly.

Update 2021: The debugger extension is called VGCRoot and I have added both the extension and the source code for the tool to my DebuggingScripts repository since Chris and Mohameds blogs no longer exist.

The VGCRoot extensions

If you are looking at a memory dump in windbg and try to figure out why your object is still sticking around, collection after collection, you can use the sos extension to run a command called !GCRoot which will give you a textual representation of how your object is rooted.

In other words, it will give you several stacks linking your object all the way up to one or more roots, where the roots may be threads, static objects, reference counted objects and until you break the chain between the root and your object your object can’t be garbage collected.

A typical root chain looks something like the one below taken from one of my memory investigation posts

0:000> !gcroot 00000000800dfb98
...
DOMAIN(000000000017E210):HANDLE(Strong):4b41308:Root: 00000000c0020990(System.Threading._TimerCallback)->
00000000c0020908(System.Threading.TimerCallback)->
00000000c001b530(System.Web.Caching.CacheExpires)->
00000000c001b568(System.Object[])->
00000000c001bef8(System.Web.Caching.ExpiresBucket)->
00000001008fbf40(System.Web.Caching.ExpiresPage[])->
00000001008fbff8(System.Web.Caching.ExpiresEntry[])->
000000010001ebd8(System.Web.Caching.CacheEntry)->
000000010001eb98(System.Web.SessionState.InProcSessionState)->
00000000800dd340(System.Web.SessionState.SessionStateItemCollection)->
00000000800dd3f0(System.Collections.Hashtable)->
00000000802fe7e0(System.Collections.Hashtable+bucket[])->
00000000800e09e8(System.Collections.Specialized.NameObjectCollectionBase+NameObjectEntry)->
00000000800dfb98(System.Xml.XmlDocument)

In this case we have an XmlDocument that can’t be garbage collected because it is stored in session scope so until it is removed from session scope (breaking the link between XmlDocument and SessionStateItemCollection) it can’t be garbage collected.

With !VGcRoot we can now get all the roots in a DGML file that we can open up in Visual Studio .NET 2010, making this much easier to digest.

You run it from windbg using !vgcroot /do /fields <objectaddress> [<DGML File path>] and the resulting graph (in this case for a Form object) looks like this.

Field Names

Here we can see for example that the Form is linked by a UserPreferenceChangedEventHandler through the _target member variable, in other words, one reason that the Form can’t be garbage collected is because it is handling a UserPreferenceChangedEvent.

I will probably be showing this off more in context in future memory investigation posts but just wanted to show off the extension so that you can start playing around with it. Be sure to check out his post to see other stuff you can do with the graph once you have it generated.

Have a good one, (and thanks to Mohamed for writing the extension)

Tess