2008-09-29 11 views
6

Wir haben einige Operationen, bei denen wir eine große Anzahl von langen String-Verkettungen durchführen und kürzlich eine Ausnahme wegen zu wenig Speicher gefunden haben. Leider ist das Debuggen des Codes keine Option, da dies bei einem Kunden vor Ort geschieht.Wie ist der RAM-Verbrauch von StringBuilder?

Also, bevor wir in eine Überarbeitung unseres Codes schauen, möchte ich fragen: Was ist der RAM-Verbrauch Eigenschaften von StringBuilder für große Saiten?

Besonders, da sie mit dem Standard-String-Typ vergleichen. Die Größe der Strings liegt weit über 10 MB, und wir scheinen mit den Problemen rund 20 MB zu kämpfen.

HINWEIS: Es geht nicht um Geschwindigkeit, sondern um RAM.

Antwort

6

Hier ist eine schöne Studie über String Concatenation vs Memory Allocation.

Wenn Sie die Verkettung vermeiden können, tun Sie es! Diese

ist ein Klacks, wenn Sie nicht haben verketten wollen aber Ihre Quellcode zu schön aussehen, verwenden Sie die erste Methode. Es wird als optimiert, wenn es eine einzelne Zeichenfolge war.

Verwenden Sie nicht + = Verketten immer. Zu viele Änderungen finden statt hinter der Szene, die nicht offensichtlich sind von meinem Code an erster Stelle. I empfehlen, lieber String.Concat() explizit mit einer Überladung (2 Strings, 3 Strings, String-Array) zu verwenden. Dies wird deutlich zeigen, was Ihr Code ohne irgendwelche Überraschungen tut, während Ihnen erlaubt, die Effizienz zu überprüfen .

Versuchen Sie, die Zielgröße eines StringBuilders zu schätzen.

desto genauer können Sie die benötigte Größe schätzen, die weniger temporäre Strings die Stringbuilder haben erstellen ihre internen Puffer zu erhöhen.

Verwenden Sie keine Format() - Methoden, wenn die Leistung ein Problem darstellt.

zu viel Aufwand ist in Parsen des Formats beteiligt, wenn Sie könnten aus Stücken ein Array konstruieren, wenn alles, was Sie verwenden sind {x} ersetzt. Format() ist gut für die Lesbarkeit, aber eines der Dinge zu gehen, wenn Sie quetschen alle mögliche Leistung aus Ihrer Anwendung.

10

Jedes Mal, wenn StringBuilder nicht genügend Speicherplatz hat, wird ein neuer Puffer doppelt so groß wie der ursprüngliche Puffer zugewiesen, die alten Zeichen kopiert und der alte Puffer wird gecodiert. Es ist möglich, dass Sie nur genug verwenden (nennen Sie es x), so dass 2x größer ist als der Speicher, den Sie zuweisen dürfen. Möglicherweise möchten Sie eine maximale Länge für Ihre Zeichenfolgen ermitteln und sie an den Konstruktor von StringBuilder übergeben, damit Sie sie vorab zuweisen können, und Sie sind der doppelten Reallokation nicht ausgeliefert.

-2

Ich weiß nicht über das genaue Speichermuster von String Builder, aber die allgemeine Zeichenfolge ist keine Option.

Wenn Sie die allgemeine Zeichenfolge verwenden, erstellt jede Verkettung ein weiteres Paar von Zeichenfolgenobjekten, und der Speicherverbrauch steigt in die Höhe, wodurch der Garbage Collector zu oft aufgerufen wird.

string a = "a"; 

//creates object with a 

a += "b" 

/creates object with b, creates object with ab, assings object with ab to "a" pointer 
+0

Ich empfehle Ihnen, die String-Klassenimplementierung zu untersuchen, bevor Sie sich darauf verlassen. Zumindest in Java ist es jetzt schlauer. Ich habe die Implementierung von .net nicht untersucht, aber ich kann nicht verstehen, warum sie nicht die gleichen Optimierungen vornehmen würden. –

1

Strigbuilder ist eine perfekte Lösung für Speicherprobleme, die durch Verkettung von Strings verursacht werden.

Um Ihre spezifische Frage zu beantworten, hat Stringbuilder einen konstanten Overhead im Vergleich zu einer normalen Zeichenfolge, bei der die Länge der Zeichenfolge der Länge des aktuell zugewiesenen Stringbuilder-Puffers entspricht. Der Puffer könnte möglicherweise doppelt so groß sein wie die resultierende Zeichenfolge, aber bei der Verkettung mit dem StringBuilder werden keine Speicherzuweisungen mehr vorgenommen, bis der Puffer voll ist. Dies ist also eine hervorragende Lösung.

Verglichen mit String ist dies hervorragend.

string output = "Test"; 
output += ", printed on " + datePrinted.ToString(); 
output += ", verified by " + verificationName; 
output += ", number lines: " + numberLines.ToString(); 

Dieser Code hat vier Saiten, die als Literale im Code gespeichert, zwei, die in den Verfahren und einem aus einer Variablen erstellt werden, aber es nutzt sechs separate Zwischenketten, die länger und länger. Wenn dieses Muster fortgesetzt wird, erhöht es die Speicherauslastung mit einer exponentiellen Rate, bis der GC ansetzt, um es aufzuräumen.

+0

Für die Person, die das abgelehnt hat, bitte erklären. – torial

3

Sie könnten sich für die Datenstruktur der Seile interessieren. Dieser Artikel: Ropes: Theory and practice erläutert ihre Vorteile. Vielleicht gibt es eine Implementierung für .NET.

[Aktualisieren, um den Kommentar zu beantworten] Verwendet es weniger Speicher? Suche Speicher in dem Artikel finden Sie einige Hinweise.
Grundsätzlich, ja, trotz der Struktur Overhead, weil es nur Speicher bei Bedarf hinzugefügt. StringBuilder muss, wenn der alte Puffer erschöpft ist, einen viel größeren Pufferspeicher (der bereits leeren Speicher verschwenden kann) und den alten leeren (der Müll wird gesammelt, kann aber immer noch viel Speicher verwenden).

Ich habe keine Implementierung für .NET gefunden, aber es gibt mindestens eine C++ - Implementierung (in SGIs STL: http://www.sgi.com/tech/stl/Rope.html). Vielleicht können Sie diese Implementierung nutzen. Beachten Sie, dass die Seite, auf die ich verwiesen habe, eine Arbeit zur Speicherleistung hat.

Beachten Sie, dass Seile nicht das Heilmittel für alle Probleme sind: ihre Nützlichkeit hängt stark davon ab, wie Sie Ihre großen Saiten bauen und wie Sie sie verwenden. Die Artikel weisen auf Vor- und Nachteile hin.

+0

Irgendwelche Daten darüber, wie es RAM-weise statt Geschwindigkeit-weise ausführt? – torial

+0

Ich antworte, indem ich meine Antwort aktualisiere. Ich hoffe, Sie werden über Antworten auf Kommentare informiert. – PhiLho