I understand that in .NET, finalizers are run even if an object is partially constructed (e.g. if an exception is thrown out of its constructor), but what about when the constructor was never run at all?
Background
I have some C++/CLI code that does effectively the following (I don't believe this is C++/CLI specific, but this is the situation I have at the ready):
try {
ClassA ^objA = FunctionThatReturnsAClassA();
ClassB ^objB = gcnew ClassB(objA); // ClassB is written in C# in a referenced project
...
}
catch (...) {...}
I have a 100% repeatable case where, if an exception is thrown out of FunctionThatReturnsAClassA(), and then a GC is triggered (seems to be reliably triggered by running this code again, but waiting a while also works), ClassB's finalizer is called.
Now, via trace output I can confirm that ClassB's constructor is not running (which is of course what you'd expect). So somehow, objB was apparently allocated and added to the finalizer list, before the preconditions for calling its constructor were even met (i.e. collecting the result from FunctionThatReturnsAClassA()).
This only happens in optimized release builds running outside the debugger. There are a variety of small changes I can make that result in the finalizer not running -- for instance inserting another method call between the two statements, or (tellingly, I think) moving the "gcnew ClassB" into a separate function that returns the object.
It seems to me that somehow the allocation part of the gcnew statement is getting reordered and run before the previous statement, but this reordering is NOT reflected in the generated MSIL code (defeating my initial assumption that this was just another C++/CLI code gen bug). Further, comparing the generated MSIL code between the "buggy" state and any of the "fixed" states shows no unexpected structural changes.
I've looked at the generated x86 code in the debugger as well and it doesn't look strange so far, but I haven't analyzed it as deeply and anyway I can't reproduce this behavior in the debugger so I'm not 100% sure the code I get from the debugger is the same as the code that shows the strange behavior.
So it could be an MSIL->x86 code gen quirk or it could be a processor instruction reordering (the former seems more likely but I haven't confirmed by trying harder to get the exact code in memory when the behavior occurs -- this is my next step).
Question
So is it valid (for lack of a better term) for the allocation of an object in .NET to be divorced and reordered separately from the constructor call for that object?
FormatterServices.GetUninitializedObject()
. BothBinaryFormatter
andDataContractSerializer
construct objects in this way. Later, if the object has a finalizer, it will get finalized. But I don't think that is what you are seeing, is it? – Boresome