Ich versuche, ein Produktionsproblem mit einem Windows-Dienst zu debuggen, der dazu neigt, schnell umzufallen, wenn mehrere gleichzeitige Verbindungen aktiv sind. Durch die Magie eines Core-Dumps und DebugDiag konnte ich feststellen, dass es eine ausstehende GC-Operation gab, die erst gestartet werden konnte, nachdem mehrere Threads mit Preemptive GC ihre Arbeit beendet hatten.
Hier ist ein Beispiel-Thread-Dump von WinDbg mit den problematischen Threads:
%Vor%Hier können Sie also mehrere Threads sehen, die preemptive GC deaktiviert haben (Threads 26,27,28,29) und eine (Thread 30), die auf diese Threads wartet, um den GC auszuführen.
Mein Google-fu führt mich zu Dieser Blogbeitrag beschreibt, was wie ein ähnliches Problem klingt, nur in meinem Fall war kein XML involviert. Es gab mir genug Informationen, um zu wissen, wo ich graben sollte, und schließlich entdeckte ich, dass eines der gemeinsamen Merkmale der Threads, bei denen der preemptive GC deaktiviert war, ein Stack-Trace war, der oben so aussah:
%Vor% DebugDiag hat mich auch vor dem CriticalSection gewarnt, und es ist einfach so, dass die Threads mit dem JIT_TailCall
auch die einzigen Threads mit RtlEnterCriticalSection
Meine Frage ist also: Ist es tatsächlich die .tail
Anweisung, die diesen Deadlock verursacht? Und wenn ja: Was kann ich dagegen tun?
Ich kann tailcalls für meine .fsproj-Dateien deaktivieren, aber es sieht so aus, als käme mindestens eine davon von FSharp.Core.dll
und einige Spelunks im Dekompiler scheinen die Existenz der .tail
-Anweisung zu bestätigen. Ich weiß also nicht, dass das changing der Projektkonfiguration alle .tail
-Anweisungen entfernt.
Hat jemand schon einmal mit so etwas zu tun?
Aktualisierung: Einige weitere Informationen, die nützlich sein könnten.
Hier ist die Ausgabe von !locks
für diesen Speicherauszug:
Thread 2340 ist der Thread, der den GC gestartet hat (Thread 30 in der Teilliste I, die oben enthalten ist).
Und !syncblk
zeigt nur Objekte an, die dem ZooKeeper-Client gehören (was zwar lästig ist, aber nicht in einen der Stacks involviert ist, die den Start von GC verhindern)
Ich bezweifle, dass Rückrufe das Problem sind (sonst würde ich vermuten, dass viele F # -Nutzer dieses Problem hätten). Von der Aufrufliste aus sieht es so aus, als würde Ihr Code auf einen kritischen Abschnitt warten, der weitaus wahrscheinlicher die Ursache des Problems ist ... Sie haben eine Ahnung, auf welche Synchronisationselemente sich Ihr Code möglicherweise stützt?
Es ist vielleicht ein bisschen spät und obwohl das Problem, das Sie beschreiben, ein bisschen anders aussieht als das, das ich hatte, deutet die Anrufspur, die Sie gegeben haben, darauf hin, dass es eine Gemeinsamkeit geben könnte.
Weitere Details finden Sie in meiner Antwort zu meiner eigenen Frage, aber kurz gesagt, es geht um die Kombination von Windows 7 und .NET 4.0-4.5, die die Tail-Rekursion in F # problematisch machen, verursachen übermäßige Sperre. Die Aktualisierung von .NET auf 4.6 oder das Aktualisieren auf Windows 8 behebt das Problem.
Da Sie darüber hinaus ein Problem mit der Garbage Collection haben, sollten Sie sich die Verwendung von Server Garbage Collection . Dies ist eines der Dinge, die ich getan habe, bevor ich das obige Problem gefunden habe und es hat einen großen Teil der Leistungsprobleme gelöst, die wir erlebt haben. Alles was benötigt wird, ist in Ihrer app.config:
%Vor%Tags und Links .net f# garbage-collection clr tail-recursion