Saturday, January 21, 2006

To Null or Not Null (Chris Lyon's Blog)

Another excellent post by Chris Lyons about the GC http://blogs.msdn.com/clyon/archive/2004/12/01/273144.aspx To Null or Not Null: GC Myth: setting an object's reference to null will force the GC to collect it right away.

GC Truth: setting an object's reference to null will sometimes allow the GC to collect it sooner.

As much as you may want to, you can't guarantee the GC will collect what you want, when you want it to. The best you can do is give it hints. The GC typically does collections when an allocation fails, not necessarily as soon as you're done with an object.

Consider the following method. What is the earliest point the GC could collect the Object that obj references?

void Foo() { Object obj = new Object(); Console.WriteLine(obj.ToString()); for (int i=0; i<10; i++) { Console.WriteLine(i); } obj = null; }

The answer depends on whether you've compiled it in Debug or Release mode. In Debug, the JIT extends all references' lifetimes to the end of their scope. This is so you don't have to worry about the GC cleaning up a variable you're trying to inspect!

In Release mode, the Object is actually first eligible for collection after the call to ToString(). The JIT is usually smart enough to realize that obj = null can be optimized away. That leaves obj.ToString() as the last reference to the object. So after the call to ToString(), the obj is no longer used and is removed as a GC Root. That leaves the Object in memory orphaned (no references pointing to it), making it eligible for collection. When the GC actually goes about collecting it, is another matter.

So when does setting a reference to null allow the object in memory to be reclaimed sooner?

Consider instance variables. Imagine a class that contains a large array of a managed type. There could be times where you no longer need that array, but you want the object to hang around. This is a good case for the Dispose pattern:

class BigObject : IDisposable { int[,] array = new int[1024,1024]; public void Dispose() { array = null; } }

Now the GC will clean up the array when it does its next collection, and you can keep that BigObject around for whatever reason, and not worry about it taking up so much memory.

No comments: