interessante OutOfMemoryException con StringBuilder

Ho la necessità di build continuamente stringhe di grandi dimensioni in un ciclo e salvarle in un database che attualmente occasionalmente produce una OutOfMemoryException .

Quello che sta praticamente succedendo qui è che creo una stringa usando XmlWriter con StringBuilder basato su 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 da sole 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.

Che cosa sta succedendo qui? Perché dovrei ottenere una 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 inserire un Thread.Sleep nel ciclo? Dovrei smettere di usare StringBuilder ? Dovresti semplicemente riprovare di fronte a una OutOfMemoryException ?

    C’è memoria ma nessun segmento contiguo in grado di gestire le dimensioni del tuo 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. È ansible chiamare GC.Collect() quando si ha una vasta collezione di oggetti.

    In realtà, quando hai un OutOfMemory, generalmente mostra un cattivo design, puoi usare il disco rigido (file temporanei) invece della memoria, non devi allocare più volte la memoria (prova a riutilizzare oggetti / buffer / …) .

    Vi consiglio FORTEMENTE di leggere questo post “Memoria insufficiente” che non fa riferimento alla memoria fisica di Eric Lippert.

    Prova a riutilizzare l’object StringBuilder durante la generazione dei dati.

    Dopo o prima dell’uso è sufficiente ripristinare 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(...); // } 

    Con le dimensioni che hai menzionato probabilmente stai correndo nella frammentazione di oggetti di grandi dimensioni (LOH).

    Riusare oggetti StringBuilder non è una soluzione diretta, è necessario prendere confidenza con i buffer sottostanti.
    Se ansible, calcolare o stimare la dimensione in anticipo e pre-allocare.

    E potrebbe aiutare se raddoppi le allocazioni, diciamo a multipli di 20k circa. Questo potrebbe migliorare il riutilizzo.