In .NET, can you run the finalizer even if the constructor of the object has never started?

I understand that in .NET finalizers are executed even if the object is partially constructed (for example, if an exception is thrown from its constructor), but what about when the constructor never started at all?

Background

I have C ++ / CLI code that effectively performs the following actions (I don’t think that this is C ++ / CLI, but this is the situation that I have in readiness):

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 an exception is thrown from FunctionThatReturnsAClassA () and then the GC starts (it seems to run reliably by running this code again, but the wait is still working), ClassB is called the finalizer.

Now, using the trace output, I can confirm that the ClassB constructor does not work (which, of course, you expect). Therefore, somehow objB was apparently allocated and added to the finalizer list before the prerequisites for calling its constructor were even fulfilled (i.e., collecting the result of FunctionThatReturnsAClassA ()).

This only happens in optimized versions of releases that run outside the debugger. There are many small changes that I can make so that the finalizer result is not executed - for example, inserting another method call between two statements or (which I think) translating "gcnew ClassB" into a separate function that returns an object.

It seems to me that somehow the gcnew command allocation part gets the reordering and is performed before the previous statement, but this reordering is NOT reflected in the generated MSIL code (defeating my initial assumption that this was another C ++ / CLI gen bug code). In addition, comparing the generated MSIL code between the “buggy” state and any of the “fixed” states does not show unexpected structural changes.

I also looked at the generated x86 code in the debugger, and so far it does not look strange, but I have not analyzed it so deeply, and in any case, I cannot reproduce this behavior in the debugger, so I'm not 100% sure that the code that I get from the debugger that matches the code that shows strange behavior.

Thus, it may be typical MSIL-> x86 gen gen code, or it may be a reordering of processor instructions (the first seems more likely, but I did not confirm, trying to get the exact code in memory when the behavior occurs - this is my next step) .

Question

So, is it really (due to the lack of a better term) for highlighting an object in .NET being bred and reordered separately from calling the constructor for that object?

+8
c # finalizer c ++ - cli
source share
1 answer

As indicated in the comments, the answer is “Yes” - the finalizer can be executed if the constructor did not start or did not execute. However, the finalizer cannot work if the distribution did not happen (which does not depend on the call to the constructor).

Now this is confirmed as a JIT optimization error: https://github.com/dotnet/coreclr/issues/2478

+1
source share

All Articles