Pregunta

Tenemos algunas operaciones en las que estamos realizando una gran cantidad de concatenaciones de cadenas grandes, y recientemente hemos encontrado una excepción de falta de memoria. Desafortunadamente, la depuración del código no es una opción, ya que esto ocurre en el sitio de un cliente.

Entonces, antes de buscar una revisión de nuestro código, me gustaría preguntar: ¿cuáles son las características de consumo de RAM de StringBuilder para cadenas grandes?

Especialmente porque se comparan con el tipo de cadena estándar. El tamaño de las cadenas supera los 10 MB y parece que nos encontramos con problemas de alrededor de 20 MB.

NOTA : No se trata de velocidad sino de RAM.

¿Fue útil?

Solución

Aquí hay un buen estudio sobre Concatenación de cadenas vs Asignación de memoria .

  

Si puede evitar la concatenación, ¡hágalo!

     

Esto es obvio, si no   tienes que concatenar pero quieres tu   código fuente para verse bien, use el   primer método Se optimizará como   si fuera una sola cadena.

     

No use + = concatenación nunca. Se están produciendo demasiados cambios   detrás de escena, que no son obvias   de mi código en primer lugar. yo   aconseja utilizar bastante String.Concat ()   explícitamente con cualquier sobrecarga (2   cadenas, 3 cadenas, matriz de cadenas).   Esto mostrará claramente cuál es tu código   lo hace sin sorpresas, mientras   permitiéndote controlar   la eficiencia.

     

Intenta estimar el tamaño objetivo de un StringBuilder.

     

Cuanto más preciso pueda estimar el   tamaño necesario, menos temporal   cadenas que el StringBuilder tendrá que   crear para aumentar su interno   buffer.

     

No use ningún método Format () cuando el rendimiento sea un problema.

     

Hay demasiada sobrecarga involucrada en   analizando el formato, cuando podrías   construir una matriz de piezas cuando   todo lo que está utilizando son {x} reemplaza.   Format () es bueno para la legibilidad, pero   una de las cosas para ir cuando estás   exprimiendo todo el rendimiento posible   de su aplicación.

Otros consejos

Cada vez que StringBuilder se queda sin espacio, reasigna un nuevo búfer del doble del tamaño del búfer original, copia los caracteres antiguos y permite que el búfer anterior se GC'd. Es posible que solo estés usando suficiente (llámalo x) de modo que 2x sea más grande que la memoria que puedes asignar. Es posible que desee determinar una longitud máxima para sus cadenas y pasarla al constructor de StringBuilder para que la preasigne y no esté a merced de la reasignación de duplicación.

Puede que le interese la estructura de datos de las cuerdas. Este artículo: Cuerdas: Teoría y práctica explica sus ventajas. Quizás haya una implementación para .NET.

[Actualización, para responder el comentario] ¿Utiliza menos memoria? Busque memoria en el artículo, encontrará algunas sugerencias.
Básicamente, sí, a pesar de la sobrecarga de la estructura, porque solo agrega memoria cuando es necesario. StringBuilder, al agotar el búfer antiguo, debe asignar uno mucho más grande (que ya puede desperdiciar memoria vacía) y descarta el antiguo (que será basura recolectada, pero aún puede usar mucha memoria mientras tanto).

No he encontrado una implementación para .NET, pero hay al menos una implementación de C ++ (en STL de SGI: http://www.sgi.com/tech/stl/Rope.html ). Quizás pueda aprovechar esta implementación. Tenga en cuenta que la página a la que hago referencia tiene un trabajo sobre el rendimiento de la memoria.

Tenga en cuenta que las cuerdas no son la cura para todos los problemas: su utilidad depende en gran medida de cómo construya sus cadenas grandes y de cómo las use. Los artículos señalan ventajas e inconvenientes.

Strigbuilder es una solución perfectamente buena para los problemas de memoria causados ??por la concatenación de cadenas.

Para responder a su pregunta específica, Stringbuilder tiene una sobrecarga de tamaño constante en comparación con una cadena normal donde la longitud de la cadena es igual a la longitud del búfer de Stringbuilder asignado actualmente. El búfer podría tener el doble del tamaño de la cadena resultante, pero no se realizarán más asignaciones de memoria al concatenar al Stringbuilder hasta que se llene el búfer, por lo que es realmente una excelente solución.

En comparación con la cadena, esto es excepcional.

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

Este código tiene cuatro cadenas que se almacenan como literales en el código, dos que se crean en los métodos y una a partir de una variable, pero utiliza seis cadenas intermedias separadas que se alargan cada vez más. Si continúa este patrón, aumentará el uso de memoria a una velocidad exponencial hasta que el GC se active para limpiarlo.

No conozco exactamente el patrón de memoria del generador de cadenas, pero la cadena común no es una opción.

Cuando usa la cadena común, cada concatenación crea otro par de objetos de cadena, y el consumo de memoria se dispara, haciendo que se llame al recolector de basura con demasiada frecuencia.

string a = "a";

//creates object with a

a += "b"

/creates object with b, creates object with ab, assings object with ab to "a" pointer
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top