merkwürdige Nicht-Arbeitsspeicher-Ausnahme während der Serialisierung

8

Ich verwende VSTS2008 + C # + .Net 3.5, um diese Konsolenanwendung auf x64 Server 2003 Enterprise mit 12G physikalischem Speicher auszuführen.

Hier ist mein Code, und ich finde, wenn die Anweisung bformatter.Serialize (stream, table) ausgeführt wird, gibt es keine Speicherausnahme. Ich überwachte die Speicherbelegung über die Registerkarte "Perormance" des Task-Managers und ich finde, dass nur 2G physischer Speicher verwendet wird, wenn eine Ausnahme ausgelöst wird, also sollte nicht zu wenig Speicher vorhanden sein. : - (

Irgendwelche Ideen, was ist falsch? Irgendwelche Einschränkungen der .Net-Serialisierung?

%Vor%

danke im voraus, George

    
George2 18.08.2009, 04:04
quelle

4 Antworten

10

Hinweis: DataTable verwendet standardmäßig das XML-Serialisierungsformat, das in 1. * verwendet wurde, was unglaublich ineffizient ist. Eine Sache zu versuchen ist Wechsel zu dem neueren Format :

%Vor%

Erneute Speicherkapazität / 2 GB; einzelne .NET-Objekte (z. B. byte[] hinter MemoryStream ) sind auf 2 GB beschränkt. Vielleicht schreibst du stattdessen in FileStream ?

(edit: nein: habe das versucht, immer noch Fehler)

Ich frage mich auch, ob Sie bessere Ergebnisse (in diesem Fall) mit table.WriteXml(stream) , vielleicht mit Kompression wie GZIP, wenn Platz ein Premium ist.

    
Marc Gravell 18.08.2009, 04:28
quelle
6

Wie bereits besprochen, ist dies ein fundamentales Problem beim Versuch, zusammenhängende Speicherblöcke in der Gigabyte-Größe zu erhalten.

Sie werden begrenzt durch (in steigendem Schwierigkeitsgrad)

  1. Die Größe des adressierbaren Speichers
  2. Die Einschränkung CLR, dass kein einzelnes Objekt mehr als 2 GB Speicherplatz beanspruchen kann .
  3. Einen zusammenhängenden Block innerhalb des verfügbaren Speichers finden.

Sie können feststellen, dass Ihnen vor dem CLR-Limit von 2 kein Speicherplatz mehr zur Verfügung steht, da der Sicherungspuffer im Stream "verdoppelnd" erweitert wird und dies schnell zur Zuweisung des Puffers im Large Object Heap führt. Dieser Heap wird nicht auf die gleiche Weise kompaktiert wie die anderen Heaps (1) und als Ergebnis zerlegt der Prozess des Aufbaus auf die theoretische maximale Größe des Puffers unter 2 das LOH, so dass Sie kein ausreichend großes zusammenhängendes finden blockieren, bevor dies geschieht.

Wenn Sie nahe an der Grenze sind, sollten Sie daher die Anfangskapazität des Streams so einstellen, dass vom Anfang an über genügend Speicherplatz über Einer der Konstruktoren .

Da Sie im Rahmen eines Serialisierungsprozesses in den Speicherstream schreiben, wäre es sinnvoll, Streams tatsächlich wie vorgesehen zu verwenden und nur die erforderlichen Daten zu verwenden.

  • Wenn Sie zu einem dateibasierten Speicherort serialisieren, streamen Sie ihn direkt dorthin.
  • Wenn dies Daten sind, die in eine SQL Server-Datenbank eingehen, sollten Sie Folgendes berücksichtigen:
  • Wenn Sie dies im Speicher serialisieren, um es zum Beispiel in einem Vergleich zu verwenden, dann ziehen Sie in Betracht, auch die Daten zu vergleichen, die verglichen werden, und zu diffundieren, während Sie weitermachen.
  • Wenn Sie ein Objekt im Speicher persistieren, um es neu zu erstellen, dann sollte dies wirklich zu einer Datei oder einer Speicherabbilddatei gehen. In beiden Fällen ist das Betriebssystem dann frei, es so gut wie möglich zu strukturieren (in Disk-Caches oder Seiten, die dem Hauptspeicher zugeordnet werden), und es ist wahrscheinlich, dass es einen besseren Job macht, als die meisten Leute tun können sich selbst.
  • Wenn Sie so vorgehen, dass die Daten komprimiert werden können, sollten Sie die Streaming-Komprimierung in Erwägung ziehen. Jeder blockbasierte Komprimierungsstrom kann ziemlich einfach in einen Streaming-Modus mit dem Hinzufügen von Auffüllen umgewandelt werden. Wenn Ihre Komprimierungs-API dies nativ nicht unterstützt, sollten Sie einen solchen verwenden, der dies tut, oder den Wrapper dafür schreiben.
  • Wenn Sie dies tun, um in einen Byte-Puffer zu schreiben, der dann angeheftet und an eine nicht verwaltete Funktion übergeben wird, dann verwenden Sie UnmanagedMemoryStream statt dessen hat dies eine etwas bessere Chance, einen Puffer dieser Art zuzuteilen, was aber immer noch nicht garantiert ist.

Wenn Sie uns vielleicht sagen, was Sie für ein Objekt dieser Größe serialisieren möchten, können wir Ihnen vielleicht bessere Möglichkeiten geben, es zu tun.

  1. Dies ist ein Implementierungsdetail, auf das Sie sich nicht verlassen sollten
ShuggyCoUk 18.08.2009 11:58
quelle
1

1) Das Betriebssystem ist x64, aber ist die App x64 (oder anycpu)? Wenn nicht, ist es auf 2 GB begrenzt.

2) Erfolgt dies "früh" oder nachdem die App für einige Zeit ausgeführt wurde (d. h. n Serialisierungen später)? Könnte es vielleicht eine Folge der großen Objekt-Heap-Fragmentierung sein ...?

    
KristoferA 18.08.2009 04:31
quelle
1

Interessanterweise geht es tatsächlich bis zu 3,7 GB, bevor hier ein Speicherfehler auftritt (Windows 7 x64). Offensichtlich würde es etwa doppelt so viel benötigen, um es fertigzustellen.

Da die Anwendung 1,65 GB nach dem Erstellen der Tabelle verwendet, scheint es wahrscheinlich, dass sie das Limit von 2 GB byte[] (oder irgendeines einzelnen Objekts) erreicht, von dem Marc Gravell spricht (1,65 GB + 2 GB ~ = 3,7 GB)

Basierend auf diesem Blog könnten Sie Ihren Speicher wahrscheinlich mithilfe von das WINAPI, und schreiben Sie Ihre eigene MemoryStream-Implementierung, die das verwendet. Das heißt, wenn Sie das wirklich wollen. Oder schreibe einen mit mehr als einem Array natürlich:)

    
Thorarin 18.08.2009 05:10
quelle