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!
Tuesday, April 11, 2006
Singletons and the law of Demeter
Monday, April 10, 2006
Background: I have been involved in several projects where some developers have used the static keywords in very bad places and caused everything from bugs to memory leaks ...
(ok, not really leaks but really nasty memory hogs...)
Rule-of-thumb: Only use static for utility methods, singleton field and singleton get:ers.
(I consider the .NET "Empty"-pattern to be a specialized singleton)
The most common miss-use of static is to create small caches, these tend to be sprinkled around the codebase with the best of intentions.
(But you know what they say about the road to hell...)
These caches are normally implemented using a simple if( ... ) statement and an assignment of a static variable if the statement evaluates to true.
// declaration
static FooBar _foobar;
// ...somewhere in the code
if( _foobar == null )
{
_foobar = GetFooBar();
}
_foobar.DoFoo();
This kind of cache is nothing but a memoryleak and a GC-killer sincethis memory behavior isn't what the GC and .NET was designed for. (the worst case I have ever seen more or less killed all the GC since there where no "dead" objects for the GC to collect and the developers stated that .NETs GC was flawed...)
So now we got an idea what not to do, lets go through what to do.
Utility methods:
A utility method is a method that doesn't use any instance fields,you will find plenty of them on the System.String class; Concat( ... ),Compare( ... ) and Join( ... ). There is even a performance gain here, static methods execute faster then instance method since the runtime doesn't need to push the "this" reference onto the stack.
Singletons:
The second legitimate use is the singleton pattern. A singleton has a very common implementation pattern.
class FooBar
{
// the one and only instance
static FooBar _instance = new FooBar();
// get:er
public static FooBar Instance { get { return _instance; } }
// the most important part, without this the class
// is possible to create with a normal new call
private FooBar() { }
}
"Empty" - singleton specialization:
To avoid multiple instances of special objects like empty strings,the .NET base class library utilizes a specialization of the singleton pattern.
And finally, since there always shall be an exception to confirm the rule, here it is:
Constant data:
that needs to bind during run-time and not compile-time; const binds at compile-time since the compiler copies the value to every user. This makes const data much faster then static but much less flexible since it will require a recompile to change. The System.IO.Path holds some data that meets this requirement. (PathSeparator, VolumeSeparatorChar)
So to conclue: avoid statics unless you are developing:
- a utility method
- a singleton class (including Empty pattern)
- defining static data that needs to bind at run-time