2008-09-29 16 views
6

Tenemos algunas operaciones en las que realizamos una gran cantidad de concatenaciones de cadenas de gran tamaño y hemos encontrado recientemente 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 del cliente.¿Cómo es el consumo de RAM de StringBuilder?

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: Esto no se trata de velocidad sino de RAM.

Respuesta

6

Aquí hay un buen estudio sobre String Concatenation vs Memory Allocation.

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

Esta es una obviedad, si no lo hace tienen para concatenar pero quiere que su código fuente se vea bien, utilizar el primer método . Se optimizará como si se tratara de una sola cadena.

No utilice + = concatenador alguna vez. Demasiado cambios están teniendo lugar detrás de la escena, que no son evidentes de mi código en el primer lugar. Me aconsejan utilizar en lugar String.Concat() explícitamente con cualquier sobrecarga (2 cuerdas, 3 cuerdas, matriz de cadenas). Esto mostrará claramente lo que su código hace sin sorpresas, mientras que permitiéndose controlar la eficiencia .

Intente estimar el tamaño de destino de un StringBuilder.

Cuanto más precisa se puede estimar el tamaño necesario , las cadenas menos temporales StringBuilder tendrá que crean para aumentar su buffer interno.

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

demasiado trabajo está involucrado en analizar el formato, cuando se podía construir una serie de piezas cuando todo lo que está utilizando son {x} reemplaza. El formato() es bueno para la legibilidad, pero es una de las cosas que debe hacer cuando está exprimiendo todo el rendimiento posible fuera de de su aplicación.

10

Cada vez que StringBuilder se queda sin espacio, reasigna un nuevo búfer dos veces el tamaño del búfer original, copia los caracteres antiguos y permite que el búfer anterior obtenga GC'd. Es posible que solo esté utilizando lo suficiente (llámelo x) de modo que 2x sea más grande que la memoria que puede asignar. Es posible que desee determinar la longitud máxima de sus cadenas y pasarla al constructor de StringBuilder para que pueda preasignar, y no estará a merced de la reasignación duplicada.

-2

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

Cuando utiliza la cadena común, cada concatenación crea otro par de objetos de cadena, y el consumo de memoria se dispara, haciendo que el recolector de basura se llame 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 
+0

que recomendamos que examine la implementación de la clase cadena antes de confiar en esto. Al menos en Java, es más inteligente que eso ahora. No he examinado la implementación de .net, pero no veo por qué no realizarían las mismas optimizaciones. –

1

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 Stringbuilder actualmente asignado. 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 cuerdas que se almacenan como literales en el código, dos que se crean en los métodos y el otro de una variable, sino que utiliza seis cuerdas intermedias separadas que se hacen más largas y más largas. Si se continúa este patrón, aumentará el uso de la memoria a una velocidad exponencial hasta que el GC entre en acción para limpiarlo.

+0

Para la persona que votó negativamente, por favor explique. – torial

3

Puede que le interese la estructura de datos de las cuerdas. Este artículo: Ropes: Theory and practice explica sus ventajas. Tal vez hay una implementación para .NET.

[Actualizar, para responder al comentario] ¿Utiliza menos memoria? Buscar memoria en el artículo, encontrará algunos consejos.
Básicamente, sí, a pesar de la sobrecarga de la estructura, ya que sólo se suma la memoria cuando sea necesario. StringBuilder, cuando agotar búfer de edad, debe asignar una mucho más grande (que ya se pueden perder memoria vacía) y deja caer el anterior (que se recoge la basura, pero todavía se puede utilizar mucha memoria en la media hora).

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

Tenga en cuenta que las cuerdas no son la solución a todos los problemas: su utilidad depende en gran medida de cómo construya las cuerdas grandes y cómo las usa. Los artículos señalan ventajas y desventajas.

+0

¿Alguna información sobre cómo funciona RAM en lugar de velocidad? – torial

+0

Respondo actualizando mi respuesta. Espero que se te notifiquen las respuestas a los comentarios. – PhiLho

Cuestiones relacionadas