2009-11-20 16 views
11

Ho la necessità di creare continuamente stringhe di grandi dimensioni in un ciclo e salvarle nel database che attualmente offre occasionalmente un OutOfMemoryException.interessante OutOfMemoryException con StringBuilder

Ciò che sta praticamente succedendo qui è creare una stringa utilizzando XmlWriter con StringBuilder in base ad alcuni dati. Quindi chiamo un metodo da una libreria esterna che converte questa stringa xml in un'altra stringa. Successivamente la stringa convertita viene salvata nel database. L'intera faccenda viene ripetuta in un ciclo di circa 100 volte per dati diversi.

Le stringhe di per sé non sono troppo grandi (inferiori a 500kByte ciascuna) e la memoria di processo non aumenta durante questo ciclo. Ma ancora, occasionalmente ottengo un OutOfMemeoryExcpetion all'interno di StringBuilder.Append. È interessante notare che questa eccezione non provoca un arresto anomalo. Posso prendere quell'eccezione e continuare il ciclo.

Cosa sta succedendo qui? Perché dovrei ottenere un OutOfMemoryException sebbene ci sia ancora abbastanza memoria libera disponibile nel sistema? Questo è un problema di heap GC?

Dato che non posso eludere la conversione di tutte queste stringhe, cosa potrei fare per rendere questo lavoro affidabile? Dovrei forzare una collezione GC? Dovrebbe mettere un Thread.Sleep nel ciclo? Devo smettere di usare StringBuilder? Dovrebbe semplicemente riprovare di fronte a un OutOfMemoryException?

risposta

14

C'è memoria ma nessun segmento contiguo che può gestire la dimensione del generatore di stringhe. Devi sapere che ogni volta che il buffer del generatore di stringhe è troppo corto, le sue dimensioni sono raddoppiate. Se puoi definire (nel cast) la dimensione del tuo costruttore, è meglio. PUOI chiamare GC.Collect() quando hai finito con una grande collezione di oggetti.

In realtà, quando si dispone di un oggetto OutOfMemory, in generale mostra un design errato, è possibile utilizzare il disco rigido (file temporanei) anziché la memoria, non è necessario allocare la memoria più e più volte (provare a riutilizzare oggetti/buffer/...).

I FORTUNATI consiglio di leggere questo post “Out Of Memory” Does Not Refer to Physical Memory da Eric Lippert.

+0

Se utilizzo lo stesso StringBuilder ripetutamente, utilizzerei lo stesso segmento di memoria (tranne quando è necessario ingrandire l'SB)? Non risolverebbe tutti i miei problemi (molto probabilmente)? – bitbonk

+0

Non sono sicuro, dovresti provare a vedere se il generatore di stringhe mantiene la sua capacità quando cancellato ... – Guillaume

+0

Non totalmente inutile: puoi evitare la frammentazione della memoria che sembra essere il vero problema qui. – Guillaume

3

Cercare di riutilizzare l'oggetto StringBuilder durante la generazione dei dati.

Dopo o prima dell'uso è sufficiente reimpostare la dimensione di StringBuilder su 0 e avviare l'aggiunta. Ciò ridurrà il numero di allocazioni e potrebbe rendere la situazione OutOfMemory molto rara.

per illustrare il mio punto:

void MainProgram() 
{ 
    StringBuilder builder = new StringBuilder(2 * 1024); //2 Kb 

    PerformOperation(builder); 
    PerformOperation(builder); 
    PerformOperation(builder); 
    PerformOperation(builder); 
} 

void PerformOperation(StringBuilder builder) 
{ 
    builder.Length = 0; 

    // 
    // do the work here builder.Append(...); 
    // 
} 
3

Con le dimensioni di cui parli si sono probabilmente in esecuzione in Large Object Heap (LOH) frammentazione.

Riutilizzare gli oggetti StringBuilder non è una soluzione diretta, è necessario ottenere una presa sui buffer sottostanti.
Se possibile, calcolare o stimare la dimensione in anticipo e pre-allocare.

E potrebbe aiutare se si arrotondano su allocazioni, diciamo a multipli di 20k circa. Questo potrebbe migliorare il riutilizzo.

+0

Perché riutilizzare oggetti StringBuilder non è una soluzione diretta? Intendi dire che riutilizzare la preallocazione iniziale (di una dimensione che si adatta a tutte le future stringhe) sarebbe la soluzione? – bitbonk

+0

bitbonk, sì, qualcosa del genere. Il modello peggiore per LOH è l'assegnazione di blocchi in dimensioni crescenti e/o alternando brevi blocchi longevi. –