Wednesday, April 26, 2006
Beware of the Finalizer() it might bite ...
Ever experienced very strange crashes at application shutdown, Null-reference exceptions from references that never should be able to bee null since they are set in the .ctor and never altered...
This might be it, I just discovered that the Finalizer thread may execute Finalize( ) even though the entire .ctor hasn't executed yet.
Some C++ (haven't tested in with C# or VB.NET yet)
ref class FooBar
{
public:
FooBar(void);
~FooBar();
!FooBar();
};
FooBar::FooBar(void)
{
Console::WriteLine( "C" );
}
FooBar::~FooBar()
{// cli/c++ Dispose()
Console::WriteLine( "~" );
}
FooBar::!FooBar()
{// cli/c++ Finalize()
Console::WriteLine( "!" );
}
void ThreadProc( Object^ state )
{
FooBar^ fb = gcnew FooBar();
}
int main(array<System::String ^> ^args)
{
WaitCallback^ wc = gcnew WaitCallback( ThreadProc );
ThreadPool::QueueUserWorkItem( wc, nullptr );
Thread::Sleep( 0 );
return 0;
}
This execution will in most cases yield this output:
C
!
Press any key to continue . . .
when executed with ctrl+F5 within VS2005, but if we alter the .ctor of the class like this...
FooBar::FooBar(void)
{
Thread::SpinWait( 100000000 );
Console::WriteLine( "C" );
}
The output will change to this:
!
Press any key to continue . . .
This means that even though the thread hasn't completed the execution of the constructor the Finalize() method will still be called by the GC during application termination. There are many possible solutions to this issue; ranging from fancy control strategies to prevent execution on arbitrary threads during shutdown to simple if statements to check that all the member fields arevalid before use, but none of these will be in place if we aren't aware of the possible issue. Hence this post!
