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 die Anwendung in einem relativ stark ausgelasteten System, gewöhnlich nach einer Laufzeit (Tage), in der dieser Serialisierungsprozess einige Zeit dauert, bekannt ist, Speicheraussetzungen zu verwerfen, wenn diese Serialisierungsmethode aufgerufen wird scheint viel Speicher zu sein. Ich vermute, dass die Fragmentierung das Problem ist (obwohl ich nicht sagen kann, ich bin 100% sicher, ich bin ziemlich nah)Soll ich GC.Collect sofort nach der Verwendung des Heapspeichers für große Objekte aufrufen, um Fragmentierung zu vermeiden

Die einfachste kurzfristige Lösung (ich denke, ich suche beide eine kurzfristige und eine langfristige Antwort) Ich kann daran denken, GC.Collect direkt nach dem Serialisierungsprozess zu nennen. 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 eng an die verbleibenden Objekte im Heap angepasst werden, ohne dass eine große Fragmentierung verursacht wird.

Anders als diese lächerliche 90MB Zuteilung glaube 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 dort und vielleicht einige andere kleinere serialisierte Objekte haben.

Irgendwelche Ideen?

-Update als Folge der guten 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, denselben 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 als nächstes die UnmanagedMemoryStream-Idee ausprobieren.

Ich würde Sie ermutigen, es auszuprobieren, wenn Sie nicht wollen. Es muss nicht genau dieser Code sein. Nur eine große Datenmenge serialisiert werden und Sie werden überraschende Ergebnisse erhalten (mir hat viele Tische, arround 15 und viele Strings und Spalten)

 byte[] bytes; 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = 
     new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();    
     System.IO.MemoryStream memStream = new System.IO.MemoryStream(); 
     serializer.Serialize(memStream, obj); 
     bytes = CompressionHelper.CompressBytes(memStream.ToArray()); 
     memStream.Dispose(); 
     return bytes; 

Update nach binäre Serialisierung mit UnmanagedMemoryStream versucht

Auch wenn ich Serialize zu einem UnmanagedMemoryStream der LOH springt 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 wir, dass ich vorbeziehe, sag, dass ich 100MB vorbeile, dann serialisiere ich, es wird 170 MB verwenden. Hier ist der Code dafür. Noch einfacher ist als der obige Code

BinaryFormatter serializer = new BinaryFormatter(); 
MemoryStream memoryStream = new MemoryStream(1024*1024*100); 
GC.Collect(); 
serializer.Serialize(memoryStream, assetDS); 

Die GC.Collect() in der Mitte gibt es nur den LOH-Leistungsindikatoren zu aktualisieren. Sie werden sehen, dass die richtigen 100 MB zugewiesen werden. Aber dann, wenn Sie die Serialisierung aufrufen, werden Sie feststellen, dass dies zu den 100 hinzugefügt wird, die Sie bereits zugewiesen haben.

+4

Gibt es etwas, was Sie tun können, um direkt zu einem 'Stream' zu serialisieren, anstatt die LOH als Puffer zu verwenden? –

Antwort

3

Leider konnte ich dies nur beheben, indem ich die Daten in Chunks zerlegte, um keine großen Chunks auf dem LOH zuzuordnen. Alle vorgeschlagenen Antworten hier waren gut und es wurde erwartet, dass sie funktionieren, aber sie taten es nicht.Es scheint, dass die Binär-Serialisierung 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.

Antwort dann auf die Frage wäre "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ß.

2

90MB 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.

4

Vorsicht, 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 doppelt so groß wie zuvor.

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.

Tun Sie dies dreimal ohne eine gen2-Sammlung und Ihr 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 Sie GC.Collect() aufrufen, nachdem Sie alle Verweise auf Null gesetzt haben, können Sie den LOH auflösen und diese Zwischenpuffer bereinigen, wobei die Gen1- und Gen2-Haufen zu früh verstopft werden.

0

Wenn Sie den LOH wirklich für einen Dienst oder etwas verwenden müssen, das für eine lange Zeit ausgeführt werden muss, müssen Sie Pufferpools verwenden, die nie freigegeben werden und die Sie beim Start optimal zuordnen können. Dies bedeutet, dass Sie Ihr "Speichermanagement" natürlich selbst 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, damit Sie nicht eine .NET API aufrufen müssen, die Sie zwingt, die Daten auf neu zugewiesenen Speicherplatz zu setzen in der LOH.

Dies ist ein guter Ausgangspunkt, Artikel über die Themen: http://blogs.msdn.com/maoni/archive/2004/12/19/327149.aspx

ich Dich sehr glücklich schätzen würde, wenn Sie würde GC Trick funktioniert, und es wäre wirklich nur funktionieren, wenn es an dem nicht viel los ist gleichzeitig im System. Wenn Sie parallel arbeiten, wird dies das Unvermeidliche nur leicht verzögern.

Auch lesen Sie 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 zur Generation n kommt.

0

Mach dir keine Sorgen über LOH Größe aufspringen. Sorgen Sie sich, LOH zuzuweisen/freizugeben. .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, wenn möglich, ist es, einmal das zuzuordnen, was Sie brauchen, und es dann nicht zu veröffentlichen - verwenden Sie es das nächste Mal wieder.

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.

0

Ich stimme einigen der anderen Poster hier zu, dass Sie versuchen könnten, Tricks zu verwenden, um mit dem .NET Framework zu arbeiten, anstatt es über GC.Collect mit Ihnen zu arbeiten zu zwingen.

Sie können diese Channel 9 video hilfreich finden, die Möglichkeiten diskutiert, um Druck auf den Garbage Collector zu erleichtern.

Verwandte Themen