I have been reading the Garbage Collection chapter from Jeffrey Richter's fine book, "CLR via C#". There, he illustrated an example of how the GC conceptually works (how roots are marked) by referring to a disassembly listing of native code emitted from the JIT compiler. From that example, it occurred to me that nesting reference types in scope seems to have ZERO effect on expediting the garbage collection of the nested variable. I wonder if I am understanding this correctly. In any case, consider these 2 versions of code:
A) Nesting a reference type variable (y) in an inner scope:
namespace scope
{
class A { public void foo() { } }
class Program
{
static void Main(string[] args)
{
A x = new A();
x.foo();
{
A y = new A();
y.foo();
}
}
}
}
B) Same as above, except that x and y are in the same scope.
namespace scope
{
class A { public void foo() { } }
class Program
{
static void Main(string[] args)
{
A x = new A();
x.foo();
A y = new A();
y.foo();
}
}
}
Out of curiosity, I checked the generated IL code for both versions, and they are the SAME!
Q1: So, this seems to imply that indeed, scoping doesn't expedite garbage collection in any way. Is this correct? (Note: I know about the "using" statement - but I'm only curious about the behaviour on the GC of "plain old scoping" as illustrated in the 2 examples above.)
Q2: If the answer to Q1 is "True", then I am utterly perplexed by how a "Object Lifetime is Not Determined by Scope" situation can possibly happen, as described here: http://www.curly-brace.com/favorite.html
Garbage collection partly depends on whether you're running in the debugger or not. I'll assume we're not.
It can actually be much more aggressive than you think. For example:
object o = new object();
Console.WriteLine(o);
Console.WriteLine("hi");
o = new object();
The object can be garbage collected immediately after the second line - i.e. long before the variable o
exits scope. This would also be true without the final line.
In other words, scoping doesn't expedite garbage collection - because the GC is already smarter than you might think.
In fact, the garbage collector can be even more aggressive. Consider this code:
Foo f = new Foo();
f.SomeMethod();
Console.WriteLine("Hello");
where Foo looks like this:
public class Foo
{
int x = 10;
public void SomeMethod()
{
Console.WriteLine(x);
for (int i = 0; i < 100; i++)
{
Console.WriteLine("Hello");
Thread.Sleep(100);
}
}
}
In theory, the garbage collector can collect the Foo
object while SomeMethod
is running so long as it's gone past the first line, where it actually reads from x
. If Foo
had a finalizer, you could have the finalizer running in one thread while SomeMethod
was running in another. Scary stuff.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With