Domanda

Abbiamo alcune operazioni in cui stiamo eseguendo un gran numero di concatenazioni di stringhe di grandi dimensioni e recentemente abbiamo riscontrato un'eccezione di memoria insufficiente. Sfortunatamente, il debug del codice non è un'opzione, in quanto ciò si verifica nel sito di un cliente.

Quindi, prima di esaminare una revisione del nostro codice, vorrei chiedere: quali sono le caratteristiche di consumo di RAM di StringBuilder per stringhe di grandi dimensioni?

Soprattutto se confrontati con il tipo di stringa standard. Le dimensioni delle stringhe superano di gran lunga i 10 MB e sembriamo incorrere in problemi di circa 20 MB.

NOTA : non si tratta di velocità ma di RAM.

È stato utile?

Soluzione

Ecco un bel studio su Concatenazione di stringhe vs allocazione di memoria .

  

Se riesci a evitare la concatenazione, fallo!

     

Questo è un gioco da ragazzi, se non lo fai   devi concatenare ma vuoi il tuo   codice sorgente per avere un bell'aspetto, usa il   primo metodo. Sarà ottimizzato come   se fosse una singola stringa.

     

Non usare + = concatenare mai. Sono in corso troppi cambiamenti   dietro la scena, che non sono ovvi   dal mio codice in primo luogo. io   consiglia di utilizzare String.Concat ()   esplicitamente con qualsiasi sovraccarico (2   stringhe, 3 stringhe, array di stringhe).   Questo mostrerà chiaramente qual è il tuo codice   fa senza sorprese, mentre   permettendoti di controllare   l'efficienza.

     

Prova a stimare la dimensione target di StringBuilder.

     

Quanto più accurato puoi stimare il   dimensione necessaria, meno temporanea   stringhe che StringBuilder dovrà   creare per aumentare il suo interno   tampone.

     

Non utilizzare alcun metodo Format () quando le prestazioni sono un problema.

     

Troppe spese generali sono coinvolte   analizzando il formato, quando potevi   costruire una matrice a pezzi quando   tutto ciò che stai usando sono {x} sostituzioni.   Format () è buono per la leggibilità, ma   una delle cose da fare quando lo sei   spremere tutte le prestazioni possibili   della tua applicazione.

Altri suggerimenti

Ogni volta che StringBuilder esaurisce lo spazio, rialloca un nuovo buffer due volte la dimensione del buffer originale, copia i vecchi caratteri e consente al vecchio buffer di ottenere GC'd. È possibile che tu stia usando abbastanza (chiamalo x) in modo tale che 2x sia più grande della memoria che ti è consentito allocare. Potresti voler determinare una lunghezza massima per le tue stringhe e passarla al costruttore di StringBuilder in modo da preallocare e non sei in balia della doppia riallocazione.

Potresti essere interessato dalla struttura dei dati delle corde. Questo articolo: Corde: Teoria e pratica spiega i loro vantaggi. Forse esiste un'implementazione per .NET.

[Aggiorna, per rispondere al commento] Usa meno memoria? Cerca memoria nell'articolo, troverai alcuni suggerimenti.
Fondamentalmente sì, nonostante l'overhead della struttura, perché aggiunge memoria quando necessario. StringBuilder, quando esaurisce il vecchio buffer, deve allocare uno molto più grande (che può già sprecare memoria vuota) e rilasciare quello vecchio (che sarà spazzatura raccolta, ma può comunque usare molta memoria nel frattempo).

Non ho trovato un'implementazione per .NET, ma esiste almeno un'implementazione C ++ (nell'STL di SGI: http://www.sgi.com/tech/stl/Rope.html ). Forse puoi sfruttare questa implementazione. Nota che la pagina di riferimento fa un lavoro sulle prestazioni della memoria.

Nota che le corde non sono la cura per tutti i problemi: la loro utilità dipende fortemente dal modo in cui costruisci le tue grandi stringhe e da come le usi. Gli articoli sottolineano vantaggi e svantaggi.

Strigbuilder è una soluzione perfettamente valida ai problemi di memoria causati dalla concatenazione di stringhe.

Per rispondere alla tua domanda specifica, Stringbuilder ha un overhead di dimensioni costanti rispetto a una stringa normale in cui la lunghezza della stringa è uguale alla lunghezza del buffer Stringbuilder attualmente allocato. Il buffer potrebbe potenzialmente avere il doppio della dimensione della stringa risultante, ma non verranno più assegnate memorie durante la concatenazione con Stringbuilder fino a quando il buffer non viene riempito, quindi è davvero un'ottima soluzione.

Rispetto alla stringa, questo è eccezionale.

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

Questo codice ha quattro stringhe memorizzate come letterali nel codice, due create nei metodi e una da una variabile, ma utilizza sei stringhe intermedie separate che diventano sempre più lunghe. Se questo schema viene continuato, aumenterà l'utilizzo della memoria a una velocità esponenziale fino a quando il GC non si avvia per ripulirlo.

Non conosco esattamente il modello di memoria del generatore di stringhe, ma la stringa comune non è un'opzione.

Quando si utilizza la stringa comune ogni concatenazione crea un'altra coppia di oggetti stringa e il consumo di memoria sale alle stelle, facendo in modo che il garbage collector venga chiamato troppo spesso.

string a = "a";

//creates object with a

a += "b"

/creates object with b, creates object with ab, assings object with ab to "a" pointer
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top