WP7: When does GC Consider a Local Variable as Garbage

 

Consider the following code that I received:

 static void Foo()
{
    TestClass t = new TestClass();
    List<object>l = new List<object>();
    l.Add(t); // Last use of l and t

    WeakReference w = new WeakReference(t);

    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine("Is Alive {0}", w.IsAlive);
}

If this code is is run on the desktop it prints Is Alive False. Very similar to the observation made by a developer in https://stackoverflow.com/questions/3161119/does-the-net-garbage-collector-perform-predictive-analysis-of-code. This happens because beyond the last usage of l and t, the desktop GC considers l and t to be garbage and collects it.

Even though this seems trivial, it isn’t. During JITing of this method the JITer performs code analysis and ensures that runtime tables are updated to reflect whether at a particular time a given local variable is alive or not (it is not used beyond that point). This needs a bunch of data structures to be maintained (lifetime tables) and also dynamic analysis of code.

In case of Windows Phone 7 (or other platforms using NETCF) for the same code you’d get Is Alive True. Which means the NETCF GC considers the object to be alive even beyond it’s last usage in the function.

Due to the limited resources available on devices, NETCF does not do these dynamic analysis. So we trade off some runtime performance to ensure faster JITing (and hence application faster startup). All local variables of all functions in the call stacks of the all managed threads in execution during garbage collection is considered live (hence also object roots) and not collect. In NETCF the moment the function Foo returns and the stack is unwound the local objects on it including t and l becomes garbage.

Do note that both of these implementations follow the ECMA specification (ECMA 334 Section 10.9) which states

For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, an implementation might (but is not required to) treat the object as no longer in use.”

Hence even though on the desktop CLR it is not important to set variables to null, it is sometimes required to do so on WP7. In case t and l was set to null GC would’ve collected them. So the guidance for WP7 is

  1. In most cases setting local variables to null is not required. Especially if the function returns fairly soon
  2. If you have locals that hold onto very large data structures and that function will remain executing for a long time then set those variables to null when you are done using them (e.g. in between the last usage of a variable in a large function and calls to a bunch of web-services in the same function which will take a long time to return).
  3. Use dispose patterns where required. It’s important on devices to free up limited resources as soon as possible.

Comments

  • Anonymous
    January 04, 2011
    Hi Abhinaba, Thanks for your article. I would like to add that if any of your readers are attempting to reproduce the result seen in your article, then be sure to use a release build configuration and also to select Debug/Start without debugging. Otherwise, like me, you may end up questioning the whole caboodle. :) Cheers, Daniel danielvaughan.orpius.com