Verhindern Sie die .NET-Speicherbereinigung für kurze Zeit

70

Ich habe eine Hochleistungsanwendung, die eine sehr große Datenmenge verarbeitet. Es empfängt, analysiert und verwirft enorme Informationsmengen in sehr kurzen Zeiträumen. Dies führt zu einer beträchtlichen Menge an Objektabwanderung, die ich derzeit zu optimieren versuche, verursacht jedoch auch ein sekundäres Problem. Wenn die Garbage Collection aktiviert wird, kann dies zu langen Verzögerungen beim Aufräumen führen (mit 10 meine ich 10 bis 100 Millisekunden). In 99% der Fälle ist dies akzeptabel, aber für kurze Zeitfenster von etwa 1 bis 2 Minuten muss ich absolut sicher sein, dass die Speicherbereinigung keine Verzögerung verursacht. Ich weiß, wann diese Zeiträume im Voraus auftreten werden, und ich muss nur sicherstellen, dass die Speicherbereinigung in diesem Zeitraum nicht erfolgt. Die Anwendung wird mit .NET 4 in C # geschrieben.

Meine Fragen sind;

  1. Ist es möglich, die Garbage Collection für das gesamte Programm kurz anzuhalten?
  2. Ist es möglich, mit System.GC.Collect () die Speicherbereinigung vor dem Fenster zu erzwingen, das ich für die Speicherbereinigung benötige, und wenn ja, wie lange bin ich für die Speicherbereinigung frei?
  3. Welchen Rat haben die Menschen, um den Bedarf an Garbage Collection insgesamt zu minimieren?

Hinweis - Dieses System ist ziemlich komplex mit vielen verschiedenen Komponenten. Ich hoffe, dass ich nicht zu einem Ansatz übergehen muss, bei dem ich für jede Klasse des Programms eine benutzerdefinierte IDisposable-Schnittstelle implementieren muss.

Drobertson
quelle
1
Dies hat einige Überschneidungen mit stackoverflow.com/questions/3074434/…
Eric
Wie würde eine benutzerdefinierte IDisposableImplementierung für jede Klasse zur Lösung Ihres Problems beitragen? Diese Objekte müssten nach der Entsorgung noch GC-geprüft werden, nicht wahr?
LukeH
4
IDisposablehat nichts mit Garbage Collection zu tun.
Dave White
@LukeH - Mit viel Arbeit mit einer IDisposable-Schnittstelle könnte ich jedes Objekt überprüfen lassen, ob sich die App in einem kritischen Zeitraum befindet, und die Finalisierung verhindern, bis dieser Zeitraum abgeschlossen ist. Dieser Ansatz würde viel Aufwand erfordern und hat VIELE mögliche Nachteile, könnte aber theoretisch verwendet werden. Sobald die kritische Phase beendet ist, wird eine Menge von der enormen Masse plötzlich finalisierter Objekte entfernt. Nicht besonders hübsch.
Drobertson
3
@LukeH Über die IDisposable-Schnittstelle fangen Sie das Objekt ab, bevor es finalisiert wird. Zu diesem Zeitpunkt überprüfen Sie eine Singleton-Referenz im Programm, um festzustellen, ob sich die App in einem kritischen Pfad befindet. In diesem Fall schließen Sie die Finalisierung kurz und fügen das Objekt zur späteren Entsorgung einer Bereinigungswarteschlange hinzu. Solange das Objekt in dieser Bereinigungswarteschlange referenziert ist, unterliegt es nicht der GC. Wenn der Status des kritischen Pfads abgeschlossen ist, werden alle Objekte in der Bereinigungswarteschlange zerstört und finalisiert. Ich mag das ehrlich gesagt überhaupt nicht und ich bin mir nicht sicher, ob es keine großen Nebenwirkungen geben würde. Es war nur ein Gedanke.
Drobertson

Antworten:

91

.NET 4.6 hat zwei neue Methoden hinzugefügt: GC.TryStartNoGCRegionund GC.EndNoGCRegionnur dafür.

Xanatos
quelle
Danke für die neuen Infos. Es wäre großartig, wenn ich dies zurück hätte, wenn dieses Problem relevant wäre. Hoffentlich hilft es neuen Menschen mit dem gleichen Problem.
Drobertson
78
GCLatencyMode oldMode = GCSettings.LatencyMode;

// Make sure we can always go to the catch block, 
// so we can set the latency mode back to `oldMode`
RuntimeHelpers.PrepareConstrainedRegions();

try
{
    GCSettings.LatencyMode = GCLatencyMode.LowLatency;

    // Generation 2 garbage collection is now
    // deferred, except in extremely low-memory situations
}
finally
{
    // ALWAYS set the latency mode back
    GCSettings.LatencyMode = oldMode;
}

Auf diese Weise können Sie den GC so weit wie möglich deaktivieren. Es werden keine großen Sammlungen von Objekten erstellt, bis:

  • Du rufst an GC.Collect()
  • Sie setzen GCSettings.LatencyModeauf etwas anderes alsLowLatency
  • Das Betriebssystem sendet ein Signal mit wenig Speicher an die CLR

Bitte seien Sie dabei vorsichtig, da die Speichernutzung in diesem tryBlock extrem schnell ansteigen kann . Wenn der GC sammelt, geschieht dies aus einem bestimmten Grund, und Sie sollten dies nur ernsthaft in Betracht ziehen, wenn Sie über eine große Menge an Speicher auf Ihrem System verfügen.

In Bezug auf Frage drei können Sie möglicherweise versuchen, Objekte wie Byte-Arrays wiederzuverwenden, wenn Sie Informationen über Dateisystem-E / A oder ein Netzwerk empfangen. Wenn Sie diese Informationen in benutzerdefinierte Klassen zerlegen, versuchen Sie auch, diese wiederzuverwenden, aber ich kann nicht zu viele gute Ratschläge geben, ohne mehr darüber zu wissen, was genau Sie tun.

Hier sind einige MSDN-Artikel, die ebenfalls helfen können:

Hinweis: GCSettings.LatencyMode = GCLatencyMode.LowLatency kann nur eingestellt werden, wenn GCSettings.IsServerGC == false. IsServerGCkann geändert werden in App.config:

  <runtime>
    <gcServer enabled="false" />
  </runtime>
Rico Suter
quelle
Ein paar Fragen, wenn Sie nichts dagegen haben. 1. Ich kenne den allgemeinen Zeitraum, in dem ein kritisches Ereignis eintreten wird, aber es wird von einem Datenelement ausgelöst, das von einer Multicast-Übertragung empfangen wird. Ich möchte verhindern, dass der GC den Empfang dieser Daten verzögert. Was empfehlen Sie? 2. Gibt es einen Leistungseinbruch, wenn Sie entweder die ConstrainedRegion aufrufen oder in den LowLatency-Modus wechseln? Könnte dies geschehen, sobald ich ein bestimmtes Datenelement sehe, ohne einen Leistungseinbruch zu erleiden? Übrigens, sehr nützliches Zeug - Danke.
Drobertson
1
Zum Beispiel würde ich sagen, wenn der allgemeine Zeitraum in der Größenordnung von einigen Sekunden klein ist, könnten Sie den GC wahrscheinlich auf einstellen, LowLatencybis die Nachricht eintrifft. Länger als das, und ich würde sagen, dass Sie meines Wissens nicht viel tun können. Wenn Sie unbedingt keine GC-Verzögerung haben müssen, können Sie versuchen, die Sendung in einem anderen Prozess zu empfangen und sie dann auf Ihren Hauptprozess zu übertragen. Dies erschwert die Sache jedoch erheblich. Für 2 ist die Eingabe einer CER meines Erachtens mit einem Leistungseinbruch verbunden, wenn Sie sie betreten und verlassen, aber ich habe sie nie gemessen oder den Latenzmodus selbst eingestellt.