Here is a great article about GC may occur at unexpected point of code execution:
Lifetime, GC.KeepAlive, handle recycling – by cbrumme http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx?wa=wsignin1.0
My question is how can I reproduce forced GC at the point mentioned in the article? I tried to put GC.Collect() at beginning of OperateOnHandle(), and defined destructor for class C but seems not working. Destructor is invoked always at end of program.
@Jon, thanks for advising me running out of debugger. Now I am able to reproduce the issue described in the article, using optimised Release build out of debugger. It proves this GC behaviour doesn’t change since .NET v1.
Code I used:
class C1
{
// Some unmanaged resource handle
IntPtr _handle = IntPtr.Zero;
static void OperateOnHandle(IntPtr h)
{
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("After GC.Collect() call");
// Use the IntPtr here. Oops, invalid operation
}
public void m()
{
OperateOnHandle(_handle);
}
~C1()
{
// Release and destroy IntPtr here
Console.WriteLine("In destructor");
}
}
class Program
{
static void Main(string[] args)
{
C1 aC = new C1();
aC.m();
}
}
Output:
In destructor
After GC.Collect() call
Bear in mind that that article is from 2003, which was using CLR v1. We’re now on CLR v4 (although there was no v3) so I’m not entirely surprised you don’t see exactly the same behaviour.
Currently I can’t even get to the linked page, and you haven’t included a description of what the page describes. Is it the possibility of an object being garbage collected before the end of an instance method?
If so, the most obvious reason you might have problems reproducing it is if you’re using the debugger. The garbage collector is much more aggressive when you’re running without a debugger attached.
Here’s a short but complete program which demonstrates the issue when running not in a debugger:
Compilation (optimized and without debug symbols, just to give it the best chance!):
Now run, with output:
Note how the finalizer runs before the method has completed.
Tested with .NET 4 and .NET 3.5.