Wie vermeidet ein Garbage Collector hier eine Endlosschleife?


101

Betrachten Sie das folgende C # -Programm, das ich als Antwort auf Codegolf eingereicht habe, um eine Schleife ohne Schleife zu erstellen:

class P{
    static int x=0;
    ~P(){
        System.Console.WriteLine(++x);
        new P();
    }
    static void Main(){
        new P();
    }
}

Dieses Programm sieht in meiner Inspektion wie eine Endlosschleife aus, scheint jedoch mehrere tausend Iterationen lang zu laufen, und dann wird das Programm ohne Fehler erfolgreich beendet (es werden keine Fehler ausgegeben). Ist es eine Spezifikationsverletzung, für die der Finalizer Pschließlich nicht aufgerufen wird?

Natürlich ist dies dummer Code, der niemals erscheinen sollte, aber ich bin gespannt, wie das Programm jemals abgeschlossen werden könnte.

Original Code Golf Post :: /codegolf/33196/loop-without-looping/33218#33218


49
Ich habe Angst, das zu machen.
Eric Scherrer

6
Dass ein Finalizer nicht aufgerufen wird, liegt sicherlich im Bereich des gültigen Verhaltens . Ich weiß nicht, warum es stört, mehrere tausend Iterationen auszuführen, ich würde null Aufrufe erwarten.

27
Die CLR ist gegen den Finalizer-Thread geschützt, der seinen Job niemals beenden kann. Es beendet es gewaltsam nach 2 Sekunden.
Hans Passant

2
Die eigentliche Antwort auf Ihre Frage im Titel lautet also, dass sie vermieden wird, indem die Endlosschleife nur 40 Sekunden lang ausgeführt wird und dann beendet wird.
Lasse V. Karlsen

4
Aus dem Versuch geht hervor, dass das Programm nach 2 Sekunden einfach alles beendet, egal was passiert. Eigentlich, wenn Sie weiterhin Threads laichen, wird es ziemlich lange dauern :)
Michael B

Antworten:


110

Laut Richter in der zweiten Ausgabe von CLR über C # (ja, ich muss aktualisieren):

Seite 478

Für (Die CLR wird heruntergefahren) wird jeder Finalize- Methode ungefähr zwei Sekunden Zeit gegeben, um zurückzukehren. Wenn eine Finalize- Methode nicht innerhalb von zwei Sekunden zurückkehrt, beendet die CLR den Prozess nur - es werden keine Finalize- Methoden mehr aufgerufen. Wenn das Aufrufen der Finalize- Methoden aller Objekte länger als 40 Sekunden dauert, bricht die CLR den Prozess einfach ab.

Wie Servy erwähnt, hat es auch einen eigenen Thread.


5
Jede Finalisierungsmethode in diesem Code dauert pro Objekt erheblich weniger als 40 Sekunden. Dass ein neues Objekt erstellt wird und dann zur Finalisierung berechtigt ist, ist für den aktuellen Finalizer nicht relevant.
Jacob Krall

2
Dies ist nicht das, was die Arbeit erledigt. Es gibt auch eine Zeitüberschreitung in der Warteschlange, die beim Herunterfahren geleert wird. In diesem Fall schlägt dieser Code fehl. Er fügt dieser Warteschlange ständig neue Objekte hinzu.
Hans Passant

Wenn Sie nur darüber nachdenken, wird die auslaugbare Warteschlange nicht wie bei "Wenn es länger als 40 Sekunden dauert, um die Finalize-Methoden aller Objekte aufzurufen, wird die CLR den Prozess einfach abbrechen."
Eric Scherrer

23

Der Finalizer wird nicht im Hauptthread ausgeführt. Der Finalizer verfügt über einen eigenen Thread, in dem Code ausgeführt wird, und es handelt sich nicht um einen Vordergrund-Thread, der die Anwendung am Laufen hält. Der Haupt-Thread wird sofort effektiv abgeschlossen. Zu diesem Zeitpunkt wird der Finalizer-Thread einfach so oft ausgeführt, wie es möglich ist, bevor der Prozess abgebrochen wird. Nichts hält das Programm am Leben.


Wenn der Finalizer 40 Sekunden nach dem Beenden des Programms nicht abgeschlossen wurde, weil kein Hauptthread aktiv ist, wird er beendet und der Prozess wird beendet. Dies sind jedoch alte Werte, sodass Microsoft möglicherweise die tatsächlichen Zahlen oder sogar den gesamten Algorithmus inzwischen angepasst hat. Se blog.stephencleary.com/2009/08/finalizers-at-process-exit.html
Lasse V. Karlsen

@ LasseV.Karlsen Ist das dokumentiertes Verhalten der Sprache oder einfach, wie MS ihre Finalizer als Implementierungsdetail implementiert hat? Letzteres würde ich erwarten.
Servy

Letzteres erwarte ich auch. Der offiziellste Hinweis auf dieses Verhalten, den ich gesehen habe, ist das, was Eric oben in seiner Antwort aus dem Buch CLR via C # von Jeffrey Richter gepostet hat.
Lasse V. Karlsen

8

Ein Garbage Collector ist kein aktives System. Es läuft "manchmal" und meistens auf Anfrage (zum Beispiel wenn alle vom Betriebssystem angebotenen Seiten voll sind).

Die meisten Müllsammler laufen in der Breite der ersten Generation in einem Subthread. In den meisten Fällen kann es Stunden dauern, bis das Objekt recycelt wird.

Das einzige Problem tritt auf, wenn Sie das Programm beenden möchten. Das ist jedoch kein wirkliches Problem. Wenn Sie killein Betriebssystem verwenden, werden Sie höflich gebeten, Prozesse zu beenden. Wenn der Prozess jedoch aktiv bleibt, kann verwendet werden, kill -9wo das Betriebssystem alle Kontrolle entfernt.

Wenn ich Ihren Code in der interaktiven csharpUmgebung ausgeführt habe, habe ich:

csharp>  

1
2

Unhandled Exception:
System.NotSupportedException: Stream does not support writing
  at System.IO.FileStream.Write (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushBytes () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushCore () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] val) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.String val) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.Write (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.SynchronizedWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.Console.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at P.Finalize () [0x00000] in <filename unknown>:0

Somit stürzt Ihr Programm ab, weil stdoutes durch die Terminierung der Umgebung blockiert wird.

Beim Entfernen Console.WriteLineund Beenden des Programms. Nach fünf Sekunden wird das Programm beendet (mit anderen Worten, der Garbage Collector gibt auf und gibt einfach den gesamten Speicher frei, ohne die Finalizer zu berücksichtigen).


Es ist faszinierend, dass der interaktive Csharp aus ganz anderen Gründen explodiert. Das ursprüngliche Programm-Snippet hatte keine Konsolen-Writeline. Ich bin gespannt, ob es auch beendet werden würde.
Michael B

@ MichaelB: Ich habe dies auch getestet (siehe Kommentar unten). Es wartet fünf Sekunden und endet dann. Ich denke, der Finalizer der ersten PInstanz ist einfach abgelaufen.
Willem Van Onsem
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.