Thursday, August 26, 2010

Inspecting Local Root Lifetime

The .NET GC determines which objects are reachable by constructing a graph of references, starting from a set of roots. One type of roots is local references that are stored on thread stacks. The GC relies on the JIT to supply it with information about the local variables in each active method, and uses that information to determine whether a root should be traversed.
When all optimizations are enabled, the JIT and GC cooperate so that local roots are relevant only as long as the local variable is still going to be used. This is a source of strange bugs—two of them are the Timer bug and the finalization race condition.
Why am I telling you all this? Well, it turns out there’s a cool SOS command called !GCInfo that can give you information on local root relevance within a method. In other words, it can tell you when (in terms of instructions) a local variable ceases to be an active local root.
For the sake of simplicity, let’s consider Debug builds only and the following method:
static void Main(string[] args)
{
   string s = "Hello World";
   Console.ReadLine();
}
Because of the Debug build, the local variable s is not going to be optimized away. Furthermore, its lifetime should extend until the end of the method, and not just the first line. Let’s verify this:
0:000> !CLRStack
OS Thread Id: 0x1f04 (0)
ESP       EIP    
[…edited for brevity…]
003ced00 704c9fbb System.IO.StreamReader.ReadBuffer()
003ced14 704c9e0c System.IO.StreamReader.ReadLine()
003ced34 70a5dd3d System.IO.TextReader+SyncTextReader.ReadLine()
003ced40 709a31c7 System.Console.ReadLine()
003ced48 005c009b FreakishTimerBug.Program.Main()
Read more: All Your Base Are Belong To Us