Here’s an interesting bit of CLR trivia for you. The CLR behaves differently with respect to how aggressively it considers references dead depending on whether or not a debugger is attached. Take the following code as an example:

public void Foo() {

    Bar b = new Bar();

    // Do some stuff with b

    for(int i = 0; i < int.MaxValue; i++) {

        DoSomething();

    }

    // What does b point to here?

    // What if we add GC.KeepAlive(b);

}

Note that the local variable, b, is not referenced in the loop or afterwards. If we are debugging the application and examine the local variable, b, after the loop, b still points to the new Bar instance created at the top of the method because b is still accessible in the current scope. If a debugger is not attached, the GC notes that b isn’t used in the loop or afterward and collects it aggressively. The reason for the difference in behaviour is for ease of debugging.

Another interesting note is the effect of GC.KeepAlive. By adding a call to GC.KeepAlive to the end of the Foo method, we are extending the lifetime of b, regardless of whether a debugger is attached or not, because we are passing the b reference to a method. The method body of GC.KeepAlive doesn’t contain any code. Its sole purpose is to keep the reference alive so that it is not collected by the GC.