2015-03-04 7 views
8

Einige Fakten: Wir haben den Dienst wcf entwickelt, der als Schicht zwischen den Clients und der Datenbank fungiert. Es ist Selfhosted und läuft als Windows-Dienst..NET Garbagecollector-Problem. Blöcke für 15-40 Minuten

Der Dienst hält mehrere Caches, wobei die größten etwa 1-2 GB im Speicher sind. Die Gesamtspeicherbelegung beträgt normalerweise 5-8 GB. Verbindungen sind Duplex und verwendet TCP-Protokoll und die Serialisierung erfolgt mit Protobuf-Net. Die Anzahl unserer verbundenen Kunden reicht normalerweise von 1000 bis 1500. Der Server ist ein 8-Core-Xeon des neuen Modells mit 64 GB Arbeitsspeicher und läuft nicht mehr als der Service.

Das Problem: Nach x Zeit, es wurde überall von einem Tag bis zu einer Woche der Service wird extrem langsam. Anfragen, die 0,5 Sekunden dauern, können eine Minute dauern. Dieses Verhalten dauert 15 bis 40 Minuten oder bis der Dienst neu gestartet wird.

Was wir getan haben: Wir haben die Netzwerk- und Netzwerkverbindung zum Server überprüft und es gibt kein Problem. Die CPU-Auslastung steigt während dieser Zeit etwas von f.eks. 30% durchschnittlich bis 40-50% durchschnittlich Wir haben Speicherauszüge gemacht und es gibt keine logischen Sperren im Code, die die Benutzer blockieren und überhaupt nicht viel Aktivität. Unsere neueste Blei ist der Müllsammler. In perfmon können wir sehen, dass "% time in gc" konstant über 90% ist, (90-97%) und die Anzahl der Aufrufe steigt. Beide GC0 und GC1. Wir vermuten, dass es auch einen blockierenden GC2 gibt, aber wir mussten den Dienst neu starten, da dieser in Produktion ist, so dass er während des 5min Fensters, in dem wir arbeiten, nicht hochgezählt hat. Die Speicherbelegung betrug 7,6 GB. Hinweis: Anrufe, die ausstehen, steigen an, so dass die Anrufe zwar ankommen, aber der Dienst sie nicht bearbeitet.

Meine Fragen sind, kann der Garbage Collector in einem Zustand, in dem es läuft und blockiert ständig über 15 Minuten? Oder hängt das Problem wahrscheinlich mit einem anderen Problem zusammen?

Unser Dienst lief GC im Workstation-Modus und Latenzmodus: Interactive Wir haben dies jetzt zu Server und SustainedLowLatency geändert und hofft, dass dies etwas helfen wird. Gibt es noch etwas, was wir tun können, wenn es der Müllsammler ist?

Edit: Die große Speicherbelegung ist von Entwurf, die Daten in den Caches ist so groß und es gibt viel mehr Speicher verfügbar.

+0

Schlagen Sie vor, die Ursache für hohe Speicherauslastung herauszufinden ... z. Versuchen Sie, "using" -Block zu verwenden, um den Speicher freizugeben, sobald Sie das Objekt fertig gestellt haben – User2012384

+0

Nur aus Neugier, wie viele Threads haben Sie? Überprüfen Sie im Task-Manager. Vor mindestens ein paar Jahren gab es ein Problem, dass je mehr Threads Sie hatten (sogar im Leerlauf), desto langsamer ist der GC – xanatos

+2

"Kann der Garbage Collector in einen Zustand kommen, in dem er ständig über 15 Minuten läuft und blockiert?" Absolut, wenn es ständig Speicher frei machen muss, aber nicht kann, weil du dich daran festhältst. Wie Raymond Chen es ausdrückte: "Ein Cache mit einer schlechten Richtlinie ist ein anderer Name für ein Speicherleck". –

Antwort

4

Übermäßige Garbage Collection wird oft durch Code-Probleme verursacht. Sie erstellen entweder zu viele Objekte in kurzer Zeit, oder Sie reservieren Speicher, ohne ihn zu veröffentlichen.

Es gibt tatsächlich eine extensive checklist available on MSDN, die Ihnen helfen sollte, das Problem zu diagnostizieren.

Ein sehr großer GC2 bedeutet, dass die darin enthaltenen Objekte mehrere Speicherbereinigungen überstanden haben, was bedeutet, dass sie für einen längeren Zeitraum im Speicher gehalten werden. Das könnte die Ursache Ihres Problems sein. Möglicherweise gibt es einen Caching-Mechanismus, der eine Optimierungs-/Aufbewahrungsrichtlinie verwenden könnte (Entfernen von Daten, die lange nicht verwendet wurden).

+0

Unser größter Cache ist mehr oder weniger eine unveränderbare Sammlung, er fügt Daten hinzu, wenn gefragt und seine fehlen. Das Element hat dann 4 Stunden ttl, wenn es nicht erneut aufgerufen wird, dann wird der Zähler zurückgesetzt. Es ist also ganz einfach. Ein häufiges Szenario ist, dass es früh am Tag zu 1-1,5GB wächst. fügt dann 0.5gb neue Daten hinzu und entfernt 0,5gb und nachts ist es völlig gelöscht. Wenn es der Cache ist, der das Problem ist, würde ich gerne in der Lage sein, dem GC zu sagen, es überhaupt während des Tages nicht zu berühren und es nachts zu scannen. Bekomme ich dieses Verhalten, wenn ich Sustainedlowlatency und dann eine gc.collect in der Nacht erzwinge? –

+0

Johan: Wenn du es einstellst, kommt der GC immer häufiger als einmal am Tag. GC ist ein fortlaufender Prozess. Beachten Sie den Abschnitt Anmerkungen zu ['GCLatencyMode'] (https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode%28v=vs.110%29.aspx): "Vollständige blockierende Sammlungen können immer noch auftreten, wenn das System unter Speicherdruck steht." –

+0

Wenn ich einen großen persistenten Cache in .net haben möchte. Wie würde ich es dann so implementieren, dass der GC meine Performance nicht ruiniert? Ich will es sagen, wenn zu überprüfen? CLR-Hosting mit behalten oder etwas? –

0

Ich habe eine ähnliche Situation. Großer Datenbankdatencache in einem Dienst, der den Protobuf mit WCF für die Clientkommunikation verwendet. Der Cache ist nicht nur für Clients gedacht, die Business-Schicht verwendet den Cache, um Operationen auszuführen. Der Speicherbedarf des Dienstes kann zwischen 2 und 10 GB liegen. Ich gebe nach 8 Stunden Inaktivität einen Teil des Caches frei. Die Maschine verfügt über 8 virtuelle Kerne und 32 GB Speicher. Ich benutze .Net 4.5.1.

Der GC würde 98% der CPU für eine Stunde verbrauchen, sobald ich den Cache von der Datenbank geladen habe. Der interessante Punkt hier in beiden Fällen gibt es keinen Erinnerungsdruck was auch immer.

Ich denke, der GC wird unabhängig davon durchgeführt, da etwas geändert wurde, wo der GC versucht, verfügbaren Speicher für alle Threads zu behalten. Da ein Thread beim Laden des Caches eine große Menge an Speicher zugewiesen hat, trat der GC ein. Ich musste mehrere Dinge tun, um das Problem zu beheben.

1) Tupel aus dem Cache entfernt. Ich benutzte sie als Wörterbuchschlüssel und ihre Implementierung von StructuralEquality ist schrecklich. Es vergleicht alle Eigenschaften als Objekte, also gibt es eine Menge Boxen für Eigenschaften, die Werte sind, und diese müssen zu irgendeinem Zeitpunkt Müll gesammelt werden.

2) Beim Ersetzen von Tupeln, die als Schlüssel verwendet wurden, konnte ich sie nicht einfach durch Strukturen ersetzen, ohne Equals zu implementieren, da der Wertvergleich Reflektionen verwendet und es zu teuer ist. Ich entschied mich dafür, Strukturen zu verwenden, um die Anzahl der Objekte zu entfernen, wenn sie in Arrays waren.

3) Um die Tupel zu entfernen, musste ich meine eigene Pair-Struktur erstellen, die die Eigenschaften mit den Standard-Equals für Eigenschaftstypen vergleicht. Im Wesentlichen das gleiche, was PowerCollections erstellt hat.