Soll GC.Collect sofort nach der Verwendung des Heapspeichers für große Objekte aufgerufen werden, um eine Fragmentierung zu verhindern

8

Meine Anwendung führt eine große Menge an binärer Serialisierung und Komprimierung großer Objekte durch. Unkomprimiert ist das serialisierte Dataset etwa 14 MB. Komprimiert ist es ca. 1,5 MB. Ich finde, dass, wenn ich die serialize-Methode auf meinem Dataset aufruft, mein großer Object-Heap-Leistungsindikator von unter 1 MB auf etwa 90 MB springt. Ich weiß auch, dass in einem relativ stark belasteten System, normalerweise nach einer Weile des Laufs (Tage), in der dieser Serialisierungsprozess einige Male stattfindet, die Anwendung bekanntlich Speicherausnahmen verwirft, wenn diese Serialisierungsmethode aufgerufen wird scheint viel Speicher zu sein. Ich vermute, dass Fragmentierung das Problem ist (obwohl ich nicht sagen kann, dass ich 100% sicher bin, bin ich ziemlich nah)

Die einfachste kurzfristige Lösung (ich denke, ich suche sowohl eine kurzfristige als auch eine langfristige Antwort) kann ich mir vorstellen, GC.Collect direkt nach dem Serialisierungsprozess aufzurufen. Dies, meiner Meinung nach, wird Müll sammeln das Objekt aus der LOH und wird es wahrscheinlich VOR anderen Objekten hinzugefügt werden können. Dadurch können andere Objekte fest an die verbleibenden Objekte im Heap angepasst werden, ohne dass eine große Fragmentierung verursacht wird.

Abgesehen von dieser lächerlichen 90MB-Zuweisung denke ich nicht, dass ich etwas anderes habe, das einen Verlust der LOH verwendet. Diese 90-MB-Zuweisung ist ebenfalls relativ selten (etwa alle 4 Stunden). Wir werden natürlich immer noch das 1,5 MB-Array und vielleicht noch andere kleinere serialisierte Objekte haben.

Irgendwelche Ideen?

Aktualisierung als Ergebnis guter Antworten

Hier ist mein Code, der die Arbeit macht. Ich habe tatsächlich versucht, dies zu ändern, um WHILE Serialisierung zu komprimieren, so dass die Serialisierung in einen Stream zur gleichen Zeit serialisiert und ich nicht viel besseres Ergebnis bekomme. Ich habe auch versucht, den Speicherstrom auf 100 MB vor zu reservieren und zu versuchen, den gleichen Strom zweimal hintereinander zu verwenden, der LOH geht sowieso auf 180 MB. Ich verwende Process Explorer, um es zu überwachen. Das ist verrückt. Ich denke, ich werde die UnmanagedMemoryStream Idee als nächstes versuchen.

Ich würde Sie ermutigen, es auszuprobieren, wenn Sie nicht wollen. Es muss nicht genau dieser Code sein. Serialisieren Sie einfach einen großen Datensatz und Sie erhalten überraschende Ergebnisse (meins hat viele Tabellen, etwa 15 und viele Strings und Spalten)

%Vor%

Aktualisierung nach dem Versuch der binären Serialisierung mit UnmanagedMemoryStream

Selbst wenn ich in einen UnmanagedMemoryStream serialisiere, springt der LOH auf die gleiche Größe. Es scheint, dass, egal was ich tue, der BinaryFormatter genannt wird, um dieses große Objekt zu serialisieren, wird das LOH verwenden. Was die Vorausplanung betrifft, scheint es nicht viel zu helfen. Sagen Sie mir, dass ich vorbeziehe, sag, dass ich 100MB vorbeile, dann serialisiere ich, es benutzt 170 MB. Hier ist der Code dafür. Noch einfacher als der obige Code

%Vor%

Die GC.Collect () in der Mitte gibt es nur um den LOH Performance Counter zu aktualisieren. Sie werden sehen, dass die richtigen 100 MB zugewiesen werden. Aber dann, wenn Sie die Serialisierung aufrufen, werden Sie feststellen, dass es scheint, dass Sie zusätzlich zu den 100, die Sie bereits zugewiesen haben, hinzuzufügen.

    
Mark 18.12.2009, 21:18
quelle

6 Antworten

3

Leider konnte ich das nur dadurch beheben, dass ich die Daten in Chunks aufteilte, um keine großen Chunks auf dem LOH zu verteilen. Alle vorgeschlagenen Antworten hier waren gut und es wurde erwartet, dass sie funktionieren, aber sie taten es nicht. Es scheint, dass die Binärserialisierung in .NET (unter Verwendung von .NET 2.0 SP2) ihre eigene kleine Magie unter der Haube tut, die Benutzer daran hindert, die Speicherzuweisung zu kontrollieren.

Beantworten Sie dann die Frage "das wird wahrscheinlich nicht funktionieren". Wenn es um die Verwendung der .NET-Serialisierung geht, ist es am besten, die großen Objekte in kleineren Blöcken zu serialisieren. Für alle anderen Szenarien sind die oben genannten Antworten groß.

    
Mark 05.03.2010, 16:01
quelle
4

Beachten Sie, wie Sammlungsklassen und Streams wie MemoryStream in .NET funktionieren. Sie haben einen zugrunde liegenden Puffer, ein einfaches Array. Immer wenn der Sammlungs- oder Strompuffer über die zugewiesene Größe des Arrays hinaus wächst, wird das Array neu zugewiesen, jetzt auf das Doppelte der vorherigen Größe.

Dies kann viele Kopien des Arrays in der LOH verursachen. Ihr 14MB-Dataset beginnt mit dem LOH bei 128KB, dann nehmen Sie weitere 256KB, dann weitere 512KB, und so weiter. Der letzte, der tatsächlich verwendete, wird ungefähr 16 MB sein. Die LOH enthält die Summe von diesen, etwa 30 MB, von denen nur eine tatsächlich verwendet wird.

Mach das dreimal ohne eine gen2-Sammlung und dein LOH ist auf 90MB angewachsen.

Vermeiden Sie dies, indem Sie den Puffer auf die erwartete Größe vorbelegen. MemoryStream hat einen Konstruktor, der eine Anfangskapazität benötigt. Also alle Sammlungsklassen. Wenn GC.Collect () aufgerufen wird, nachdem Sie alle Verweise auf Null gesetzt haben, können Sie den LOH auflösen und diese Zwischenpuffer bereinigen, wobei Sie die Haufen von gen1 und gen2 zu früh verstopfen.

    
Hans Passant 18.12.2009 21:51
quelle
2

90 MB RAM ist nicht viel.

Vermeiden Sie den Aufruf von GC.Collect, es sei denn, Sie haben ein Problem. Wenn Sie ein Problem haben und keine bessere Lösung finden, versuchen Sie, GC.Collect aufzurufen und nachzusehen, ob Ihr Problem gelöst ist.

    
Brad 18.12.2009 21:48
quelle
0

Wenn Sie das LOH wirklich für so etwas wie einen Dienst oder etwas verwenden müssen, das für eine lange Zeit ausgeführt werden muss, müssen Sie Pufferpools verwenden, die niemals freigegeben werden und die Sie idealerweise beim Start zuordnen können. Dies bedeutet, dass Sie Ihr "Speichermanagement" natürlich selbst dafür durchführen müssen.

Je nachdem, was Sie mit diesem Speicher tun, müssen Sie eventuell auch den nativen Code für ausgewählte Teile aufrufen, um zu vermeiden, dass Sie eine .NET-API aufrufen müssen, die Sie zwingt, die Daten auf neu zugewiesenen Speicherplatz zu setzen die LOH.

Dies ist ein guter Ausgangspunkt zu den Themen: Ссылка

Ich würde Sie sehr glücklich schätzen, wenn Ihr GC-Trick funktionieren würde, und es würde wirklich nur funktionieren, wenn nicht viel zur gleichen Zeit im System läuft. Wenn Sie parallel arbeiten, wird dies das Unvermeidliche leicht verzögern.

Lesen Sie auch in der Dokumentation über GC.Collect.IIRC, GC.Collect (n) sagt nur, dass es nicht weiter sammelt als die Generation n - nicht, dass es tatsächlich jemals Generation n erreicht.

    
Rovpedal 18.12.2009 22:05
quelle
0

Mach dir keine Sorgen, dass die LOH-Größe aufspringt. Sorgen Sie sich, LOH zuzuordnen / aufzuheben. .Net sehr dumm über LOH - anstatt LOH-Objekte weit weg von regulären Heap zuzuordnen, es auf der nächsten verfügbaren VM-Seite zugeordnet. Ich habe eine 3D-App, die viel von LOH und regulären Objekten zuordnet / löscht - das Ergebnis (wie im DebugDiag-Dump-Bericht zu sehen ist) besteht darin, dass Seiten mit kleinem Heap und großem Heap im RAM alternieren, bis keine großen Chunks mehr vorhanden sind der Anwendungen 2 GB VM-Platz übrig. Die Lösung ist, wenn möglich, einmal das zuzuordnen, was Sie brauchen, und dann nicht zu veröffentlichen - verwenden Sie es das nächste Mal.

Verwenden Sie DebugDiag, um Ihren Prozess zu analysieren. Sehen Sie sich an, wie sich die VM-Adressen allmählich der 2-GB-Adressmarke nähern. Nehmen Sie dann eine Änderung vor, die das verhindert.

    
ToolmakerSteve 14.01.2010 20:46
quelle
0

Ich stimme einigen der anderen Poster hier zu, dass Sie möglicherweise versuchen möchten, Tricks zu verwenden, um mit .NET Framework zu arbeiten, anstatt zu versuchen, es mit GC.Collect zu arbeiten.

Sie finden diese Channel 9-Video hilfreich, das Möglichkeiten diskutiert, den Druck auf den Müllsammler zu verringern.

    
David Silva Smith 14.01.2010 20:56
quelle