As a .NET developer, your top three concerns are 1) Memory, 2) Memory, and the last one is the toughest, 3) Memory. While .NET takes away the pain of manual memory management if you aren't constantly worrying about what's in your Gen 2 heap, you're going to suffer with bad performance or worse, unexplained OutOfMemoryExceptions. The garbage collection system in .NET is fantastic, but when you trigger a garbage collection, none of your code is executing. That's why it's so important to have that feel for which of your objects are sitting in which generation.

While you can use WinDBG and Steve Johnson's excellent SOSEX extension to look up your objects, it's a bit painful. My ultimate dream would be that the CLR team would expose a heap walking functionality in the debugging API so this was a natural part of the Visual Studio debugger. While that may happen in a future version, I wanted to show you a way you can get this today. While I'll show this with Visual Studio 2008, everything should work exactly the same in Visual Studio 2005.

Finding an object's generation in your code is easy with the GC.GetGeneration function. Just pass the object as a parameter and the return value will be 0, 1, or 2. Since the debugger has supported calling functions from the debugger Watch window forever, there's nothing stopping you from calling GC.GetGeneration on your own. In fact, the debugger wants you call functions from the Watch window so they provide Intellisense and tool tip support to make it easy.

If I wanted to find out what generation the sb variable is in, just pass it as the parameter.

If the object you want to look at is in scope, calling GC.GetGeneration on it works great. The bigger question is how can you look at those objects that are not in scope? That's probably the case that you're most interested in seeing. Fortunately, there's a barely mentioned technique you can use to observe your interesting objects: Make Object ID.

In any debugger window where the expression evaluator is used, such as the Watch, Me/This, Locals, Quick Watch, and Data Tips, you can right click on the variable to bring up the context menu where you can access the Make Object ID command.

After selecting Make Object ID, you'll see that in the Value column or Data Tip display, there will be a decimal integer followed by the number (#) sign. That's the ID applied to that object. Note that Make Object ID will not add a reference to the object inside the debuggee. Think of Make Object ID as watching the object, but not referencing it.

In the screen shot below, I created an Object ID for the XmlDocument variable doc. That added the {1#} in the value column after the type. What's nice is that the Object ID can be viewed directly. In the second row, I typed in 1# so I could continue to observe that XmlDocument no matter where I stop later in the debugger. The important point of Make Object ID is that you can observe objects even when they are not in scope!

You might be wondering what happens if you add an Object ID to a second object in your program. What happens is that the second Object ID will be 2#, the third 3# and so on. If you want to observe thirty or forty objects, you'll need to put everything down in a table so you can keep them straight. I would love to see the ability to provide your own names to Object IDs in a future version of the debugger.

What I especially like about making an Object ID is that if the object you're watching is garbage collected, you'll see the that when you stop in the debugger. You may need to click the green recycle icon on the right hand side of the Value column if the line is grayed out when you stop.

While Make Object ID lets you observe objects outside of the current scope and see if its garbage collected, how does that help with determining the generation? The good news is that it helps tremendously as the expression evaluator in Visual Studio seems to know about the Object ID values and can parse them.

The documentation does not explicitly state that using the Object ID in different types of expressions is supported but in the discussion of Make Object ID, it says that you can use Make Object ID to set per instance breakpoints. Unlike native code, you don't have any object addresses while debugging in .NET. In native code, you'd set a per instance breakpoint with a condition of this == 0x1234567, where 0x1234567 is the address of the instance pointer. For .NET, add an Object ID to the this/Me of the object you want to stop on. In the breakpoint condition, you, if the Object ID is 5#, you'd enter this == 5# or Me = 5# for C# or VB, respectively.

I figured it could not hurt to try passing the Object ID to GC.GetGeneration to see what would happen.

Excellent! The expression evaluator does the correct parsing and you now have a way to watch what generation an object while using Visual Studio even if it's not in scope. While it's still a manual process, it might let you stay in Visual Studio a bit longer before you have to do the deep spelunking with SOSEX. Hopefully future versions of Visual Studio will make it easy to determine our object's generation, but at least you have a tip that works with today's debugger.