interesante OutOfMemoryException con StringBuilder

Tengo la necesidad de construir continuamente cadenas grandes en un bucle y guardarlas en una base de datos que ocasionalmente produce una OutOfMemoryException .

Lo que básicamente está sucediendo aquí es que creo una cadena usando XmlWriter con StringBuilder basado en algunos datos. Luego invoco un método de una biblioteca externa que convierte esta cadena xml en alguna otra cadena. Después de eso, la cadena convertida se guarda en la base de datos. Todo esto se hace repetidamente en un bucle de aproximadamente 100 veces para datos diferentes.

Las cadenas en sí no son demasiado grandes (por debajo de 500kByte cada una) y la memoria del proceso no aumenta durante este ciclo. Pero aún así, ocasionalmente obtengo una OutOfMemeoryExcpetion dentro de StringBuilder.Append . Curiosamente, esta excepción no da como resultado un locking. Puedo atrapar esa excepción y continuar el ciclo.

¿Que esta pasando aqui? ¿Por qué debería obtener una OutOfMemoryException todavía hay suficiente memoria libre disponible en el sistema? ¿Es esto un problema de montón de GC?

Dado que no puedo eludir la conversión de todas estas cadenas, ¿qué podría hacer para que esto funcione de manera confiable? ¿Debo forzar una colección de GC? Debería poner un Thread.Sleep en el bucle? ¿Debo dejar de usar StringBuilder ? ¿Debería simplemente volver a intentarlo cuando se enfrente con una OutOfMemoryException ?

Hay memoria, pero no hay un segmento contiguo que pueda manejar el tamaño de su generador de cadenas. Debe saber que cada vez que el buffer del generador de cadenas es demasiado corto, su tamaño se duplica. Si puede definir (en el ctor) el tamaño de su constructor, es mejor. PUEDE llamar a GC.Collect() cuando haya terminado con una gran colección de objetos.

En realidad, cuando tienes una OutOfMemory, generalmente muestra un diseño incorrecto, puedes usar el disco duro (archivos temporales) en lugar de memoria, no debes asignar memoria una y otra vez (intenta reutilizar objetos / almacenamientos intermedios / …) .

Le recomiendo encarecidamente que lea esta publicación “Sin memoria” que no se refiere a la memoria física de Eric Lippert.

Intenta reutilizar el objeto StringBuilder cuando hagas la generación de datos.

Después o antes del uso, simplemente reinicie el tamaño de StringBuilder a 0 y comience a anexar. Esto disminuirá el número de asignaciones y posiblemente hará que la situación OutOfMemory sea muy rara.

Para ilustrar mi 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 los tamaños que mencionas, probablemente te encuentres con la fragmentación de Large Object Heap (LOH).

La reutilización de objetos StringBuilder no es una solución directa, es necesario controlar los almacenamientos intermedios subyacentes.
Si es posible, calcule o estime el tamaño de antemano y preasigne.

Y podría ser útil si redondeas las asignaciones, digamos a múltiplos de 20 k más o menos. Eso podría mejorar la reutilización.