Laden...

RAM-Verbrauch bei JSON-Serialisierung großer Objekte optimieren

Erstellt von fungi35 vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.417 Views
F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren
RAM-Verbrauch bei JSON-Serialisierung großer Objekte optimieren

Hallo Leute,

ich muss ein sehr großes Objekt (mit vielen enthaltenen Listen und weiteren Objekten) serialisieren und im Programmverlauf wieder deserialisieren (dieser Vorgang wird oft ausgeführt). Momentan wird das Objekt in einen JSON-String (Newton) serialisiert. Es scheint so, bzw. ich habe beobachtet, dass das ganze auf Dauer sehr RAM-intensiv ist.

Ist es so, dass ständiges bilden großer Strings (nichts anderes macht die Serialisierung in diesem Fall ja) viel RAM verbraucht? Kann man die Serialisierung und Deserialisierung großer Objekte auch so gestalten, dass nicht viel RAM für das Serialisieren des Objekts (= bisherige String-Serialisierung) verbraucht wird?

Sorry, wenn ich mich zu kompliziert ausgedrückt habe 😉

Gruß fungi

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo fungi35,

Ist es so, dass ständiges bilden großer Strings (nichts anderes macht die Serialisierung in diesem Fall ja) viel RAM verbraucht?

Ja das ist so. V.a. dann wenn der StringBuilder nicht zum Einsatz kommt.
Bei Strings wird bei jeder Verkettung, Trim, Insert, ... ein neues Objekt erstellt und da hat der GC dann jede Menge zu tun.

Ob und inwiefern Newtonsoft hier Möglichkeiten zum Eingriff in die Serialisierung geschaffen hat weiß ich nicht, aber das lässt sich nachlesen.

Sonst kannst du dir überlegen ob du nicht in andere Formate serialisiert, die besser für große Datenmengen geeignet sind.
Wenns rein .net ist, so kannst du auch binäre serialisieren.
Sonst schau dir Google Protobuf an.

Du kannst aber auch das sehr große Objekt in kleinere Teile zerlegen, diese separat serialisieren und bei Bedarf zum Großen und Ganzen zusammenbauen. Am besten per eigenen Builder der sich darum kümmert.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

5.657 Beiträge seit 2006
vor 6 Jahren

Hi fungi35,

ein sehr großes Objekt (mit vielen enthaltenen Listen und weiteren Objekten) serialisieren und im Programmverlauf wieder deserialisieren

Geht es dabei um Datenübertragung oder -speicherung oder um etwas anderes? Je nach Anwendungsfall sind verschiedene Strategien denkbar, um die Datenmenge bzw. den Speicherverbrauch zu reduzieren.

Weeks of programming can save you hours of planning

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

Über die Serialisierung wird im Prinzip eine Objekt-Transaktion implementiert.
Beim Starten der Bearbeitung wird das Objekt serialisiert, beim Abbrechen der Bearbeitung wird es wieder deserialisiert und somit in seinen alten Zustand versetzt. Das dauert auch teilweise einen Moment, was ich auch gerne verbessern würde weil es die Usability und den Arbeitsfluss stört.

Ich hatte auch schon darüber nachgedacht, das ganze per Stream direkt in eine Datei zu schreiben und mir so den Weg über den riesigen String im Speicher zu sparen. Da stellte sich mir aber die Frage, ob diese Vorgehensweise die Performance wegen des Festplattenzugriffs nicht verschlechtern würde...

Sonst kannst du dir überlegen ob du nicht in andere Formate serialisiert, die besser für große Datenmengen geeignet sind.
Wenns rein .net ist, so kannst du auch binäre serialisieren.
Sonst schau dir Google Protobuf an.

Google Protobuf sieht sehr interessant aus. Meinst Du, das könnte für meinen oben beschriebenen Anwendungsfall eine gute Anwendung sein?

Welche Vorgehensweise macht generell Sinn, wenn man große Inhalte "zwischenlagern" möchte, ohne das die Anwendung irgendwann einen sehr hohen Speicherverbrauch hat?

849 Beiträge seit 2006
vor 6 Jahren

Hast Du schon mal über eine Datenbank nachgedacht? Abhängig von der Form deiner Daten, kannst Du hier immer nur das laden und schreiben was Du gerade bearbeitest. Ist auch vielleicht besser falls jemand deine Anwendung nicht abbricht, sondern hart abschiesst bzw. Stromausfall etc.

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

Das ganze auf Datenbank-Transaktionen umzustellen kommt erstmal nicht in Frage, weil der Aufwand dafür viel zu hoch wäre.

T
2.219 Beiträge seit 2008
vor 6 Jahren

@fungi35
Das halte ich für eine sehr gewagte Aussage.
Wenn eine Umstellung zu Aufwändig ist, dann ist der Code Mist.
Habt ihr euch dann an Basis Prinzipieen wie das Drei Schichten Model gehalten?
Oder wurde hier Code wild in die UI zusammen gepackt?

Ich sehe hier auch keinen sinnvolleren Weg als über eine DB.
In eurem Fall wahrscheinlich eine Anwendungsdatenbank ala Sqlite.
Serialisierung sollte man eher zur Datenübertragung als zur Datenhaltung nutzen.
Gerade bei großen Objekten, die intern eine tiefere Hierachie haben, kommt man mit Serialisierung nicht weit.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

Ja, das Programm ist im Drei-Schichten-Model umgesetzt. Der Datenbankzugriff ist komplett von der UI getrennt.

Es wäre trotzdem nicht so leicht, das ganze auf Datenbank-Transaktionen umzustellen:

  • Ein Auftrag wird geladen
  • Die Positionen werden geladen (es geht in diesem Thread um die Bearbeitung der Positionen)

Auf DB-Transaktionen umzustellen würde bedeuten, dass beim Starten der Bearbeitung einer Position eine Transaktion gestartet würde, alle Änderungen die an der Position gemacht werden direkt in die Datenbank übernommen werden müssten und die Transaktion anschließend mittels COMMIT oder ROLLBACK beendet werden würde.
Mal angenommen, der Benutzer verwirft seine Änderungen und die Transaktion wird zurückgenommen, dann müsste man auch die Daten der Position aus der Datenbank neu laden, was wiederum auch keinen Geschwindigkeitsvorteil bringen würde. Desweiteren würden wir die Fähigkeit verlieren, das alle Änderungen NICHT übernommen werden, wenn man den gesamten Vorgang (in dem die Positionen enthalten sind) abbricht.

3.003 Beiträge seit 2006
vor 6 Jahren

Wurde ja schon gesagt, dass die Zeichenketten Ursache des Speicherproblems sind, das du hast. Dann serialisierst/deserialisierst du halt in einen (File-)Stream.


//there....

MyClass sourceObject = GetLargeObject();

JsonSerializer serializer = new JsonSerializer();
//Converter je nach Gusto hinzufügen, serializer konfigurieren....

using (var sw = new StreamWriter(filePath))
using (var writer = new JsonTextWriter(sw))
    serializer.Serialize(writer, sourceObject);


//...and back again.

MyClass destinationObject;

using (StreamReader file = File.OpenText(filePath))
{
    JsonSerializer serializer = new JsonSerializer();
    destinationObject = (MyClass)serializer.Deserialize(file, typeof(MyClass));
}

LaTino

EDIT: geht auch mit einem MemoryStream, und ich würde fast behaupten, auch da kannst wesentlich größere Objekte behandeln als auf deine bisherige Weise. Muss also kein File sein.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

Danke LaTino, das werde ich direkt ausprobieren 😃

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo fungi35,

wenn du Serialisierung für transaktionales Verhalten verwenden willst, so nimm doch binäre Serialisierung (BinaryFormatter), dann umgehst du das komplette String-Problem.
Allgemeiner: Es gibt jede Menge Serialisieren, d.h. für deinen Anwendungsfalls musst du per Benchmark den für dich passendensten ermitteln.

Das wäre aber eher ein Backup- und Restore-Verhalten. Für Transaktionen werden i.d.R. Command-Patterns mit Undo/Redo umgesetzt. Das ist zwar etwas aufwändiger, dafür hast du aber die Möglichkeit einzelne Schritte rückgängig zu machen / wiederherstellen anstatt einem alles od. nichts.
Suche mal im Forum danach, da gibt es ein paar Themen und (generische) Ansätze dafür, welche sich leicht integrieren lassen.

Das dauert auch teilweise einen Moment, was ich auch gerne verbessern würde weil es die Usability und den Arbeitsfluss stört.

Das passiert hoffentlich asynchron, im Sinne von [FAQ] Warum blockiert mein GUI?

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

H
523 Beiträge seit 2008
vor 6 Jahren

Wäre das nicht auch ein passender Einsatzzweck für den RecyclableMemoryStream bzw. den RecyclableMemoryStreamManager von Microsoft?