2015-12-24 12 views
8

Ich verstehe, dass in .NET Finalizer ausgeführt werden, selbst wenn ein Objekt teilweise konstruiert ist (z. B. wenn eine Ausnahme aus dem Konstruktor geworfen wird), aber was ist, wenn der Konstruktor überhaupt nicht ausgeführt wurde?Kann in .NET ein Finalizer ausgeführt werden, selbst wenn der Konstruktor eines Objekts nie ausgeführt wurde?

Hintergrund

Ich habe einige C++/CLI-Code, der effektiv das tut folgenden (ich glaube nicht, das C++/CLI spezifisch ist, aber das ist die Situation, die ich in Bereitschaft haben):

Ich habe einen 100% wiederholbaren Fall wo, wenn eine Ausnahme aus FunctionThatReturnsAClassA() ausgelöst wird, und dann ein GC ausgelöst wird (scheint zuverlässig durch diesen Code erneut ausgeführt wird, aber eine Weile funktioniert auch funktioniert) Der Finalizer von ClassB wird aufgerufen.

Jetzt kann ich über Trace-Ausgabe bestätigen, dass der Konstruktor von ClassB nicht ausgeführt wird (was natürlich zu erwarten ist). Irgendwie wurde objB anscheinend zugewiesen und der Finalizer-Liste hinzugefügt, bevor die Vorbedingungen für den Aufruf seines Konstruktors erfüllt wurden (d. H. Das Ergebnis von FunctionThatReturnsAClassA() gesammelt wurde).

Dies geschieht nur in optimierten Release-Builds, die außerhalb des Debuggers ausgeführt werden. Es gibt eine Vielzahl von kleinen Änderungen, die ich machen kann, dass der Finalizer nicht ausgeführt wird - zum Beispiel Einfügen eines anderen Methodenaufrufs zwischen den beiden Anweisungen, oder (glaube ich, denke ich) Verschieben des "gcnew ClassB" in eine separate Funktion, die das zurückgibt Objekt.

Es scheint mir, dass irgendwie der Zuordnungsteil der gcnew-Anweisung neu geordnet und vor der vorherigen Anweisung ausgeführt wird, aber diese Neuordnung spiegelt sich nicht in dem generierten MSIL-Code wider (meine ursprüngliche Annahme, dass dies nur ein anderes C++ war) CLI-Code gen Bug). Außerdem zeigt der Vergleich des generierten MSIL-Codes zwischen dem "fehlerhaften" Zustand und einem der "festen" Zustände keine unerwarteten strukturellen Veränderungen.

Ich habe mir auch den generierten x86-Code im Debugger angeschaut und er sieht bisher nicht sonderlich komisch aus, aber ich habe ihn nicht so tiefgehend analysiert und trotzdem kann ich dieses Verhalten im Debugger so nicht reproduzieren Ich bin nicht 100% sicher, dass der Code, den ich vom Debugger bekomme, derselbe ist wie der Code, der das seltsame Verhalten zeigt.

So könnte es ein MSIL-> x86-Code gen Quirk sein oder es könnte ein Prozessor Anweisung neu zu ordnen (ersteres scheint eher, aber ich habe nicht durch härter versucht, den genauen Code im Speicher zu erhalten, wenn das Verhalten auftritt - Das ist mein nächster Schritt).

Frage

So gilt es (mangels eines besseren Begriffs) für die Zuweisung eines Objekts in .NET geschieden werden und getrennt vom Konstruktoraufruf für das Objekt neu geordnet?

+3

Es ist möglich, dass ein Objekt ohne Aufruf eines seiner Konstrukteure zugeteilt werden durch den Aufruf [ 'FormatterServices.GetUninitializedObject()'] (https:

ein JIT-Optimierung Bug bestätigt, dass es Dies ist nun // msdn .microsoft.com/de-us/library/system.runtime.serialization.formatterservices.getuninitializedobject.aspx). Sowohl 'BinaryFormatter' als auch' DataContractSerializer' konstruieren Objekte auf diese Weise.Später, wenn das Objekt einen Finalizer hat, wird es finalisiert. Aber ich glaube nicht, dass Sie das sehen, oder? – dbc

+0

Es ist nicht genau das, was ich sehe, aber es hebt hervor, dass das Framework Objekte auf diese Weise erstellen kann und es zumindest theoretisch möglich ist, dass etwas zwischen der Zuweisung und der Konstruktion unterbrochen werden könnte. Es wird jedoch nicht beantwortet, ob es zulässig ist, dass diese beiden Phasen getrennt werden, wenn sie als Teil einer neuen Standard/gcnew-Anweisung ausgeführt werden. – rationull

+0

Jitter Optimizer Bugs können passieren. Aber nicht diese, die Objektzuordnung (die den Finalizer zum Ausführen benötigt) und der Konstruktoraufruf ist ein einzelner Aufruf an eine CLR-Hilfsfunktion. Ein einzelner Anruf kann Probleme nicht neu ordnen. Sie benötigen eine gute Repro mit dokumentierter CLR-Versionsnummer und telefonieren mit Microsoft Support, um weiter zu kommen. –

Antwort

1

Wie in Kommentaren beantwortet, ist die Antwort "Ja" - ein Finalizer kann ausgeführt werden, wenn ein Konstruktor nicht ausgeführt oder nicht abgeschlossen wurde. Ein Finalizer kann jedoch nicht ausgeführt werden, wenn keine Zuweisung erfolgte (unabhängig vom Aufruf des Konstruktors). https://github.com/dotnet/coreclr/issues/2478