2010-04-20 15 views
16

El otro día estaba investigando una fuga de memoria que aumentaba la aplicación de ~ 50MB a ~ 130MB en menos de dos minutos. Resulta que el problema fue con la clase ConcurrentQueue. Internamente, la clase almacena una lista vinculada de matrices. Cuando un artículo se retira de la Cocurrencia Concurrente, el índice en la matriz se topa, pero el elemento permanece en la matriz (es decir, no está configurado como nulo). El nodo de matriz completo se descarta después de suficientes enqueues/dequeues, por lo que no es técnicamente una fuga , pero si se colocan objetos grandes en ConcurrentQueue, esto puede salirse de control rápidamente. La documentación no toma nota de este peligro..NET Framework - ¿Posibles clases con pérdida de memoria?

Me preguntaba ¿qué otras posibles fallas de memoria existen en la biblioteca de clases base? Sé acerca de la subcadena (es decir, si llamas a subcadena y solo retienes el resultado, toda la cadena todavía estará en la memoria). ¿Alguna otra que te hayas encontrado?

Respuesta

0

Aunque no es una pérdida de memoria directa o específica para .net/BCL, existe la concatenación de cadena (con el operador + =). Eso es bastante CPU intensivo en bucles debido a la recolección de basura pesada.

+0

Sip. Java resolvió esto convirtiéndolo para usar un StringBuilder; No sé por qué .NET no ha hecho lo mismo. –

+0

.NET tiene lo mismo que en System.Text – Aren

+2

El compilador VS C# también es lo suficientemente inteligente como para convertir expresiones como ("This" + "is" + "string #" + stringNumber) en StringBuilder internamente, si tiene sentido para hacerlo –

11

Estás en la correcta. El error se encuentra en el método System.Collections.Concurrent.ConcurrentQueue<T>+Segment.TryRemove(out T, ref ConcurrentQueue<T>.Segment).

Si nos fijamos en este método en el reflector, verá la siguiente línea:

result = this.m_array[low]; 

No debería ser la siguiente línea después de que:

this.m_array[low] = default(T); 

Como referencia, se puede ver cómo esto se implementa correctamente en el método System.Collections.Generic.Queue<T>.Dequeue().

+0

Sí; Vi esto también en Reflector, aunque no estoy seguro de si de alguna manera se rompería la atomicidad/el enhebrado. –

+1

No, definitivamente es un error. El depósito está protegido en esa región y el código está diseñado para garantizar que el valor que contiene solo se lea una vez. –

+1

Por cierto, originalmente envié un informe sobre esto a alguien en MS el 1 de abril. Simplemente no creo que hayan tenido la oportunidad de solucionarlo todavía. Desafortunadamente, la clase es completamente inutilizable hasta que se resuelva el problema. –

-1

ConcurrentQueue contendrá solo un máximo de 31 objetos abandonados. Esto no debería representar un gran problema a menos que se trate de gráficos de objetos realmente grandes.

De todos modos, no tiene sentido utilizar un ConcurrentQueue si usted no tiene bastante espacio para asignar 32 objetos ...

+0

¿Qué sucede si tengo cola de mensajes? ¿Qué pasa si es para la conexión del cliente del socket del servidor, de modo que haya 500 de tales colas? Y cada cliente decide enviarme 32 mensajes cada 10 KB de largo. Termino con 160 MB de basura. Y si tengo 2000 clientes o los mensajes se vuelven más grandes, termino obteniendo OutOfMemoryException y un programa bloqueado. Y oye, si trato de reutilizar estas colas de mierda, y preasignar montones de ellas, entonces tengo esta basura recordada incluso después de que se cierra la conexión del socket. Claramente, tiene sentido usar esta cola, cuando no tiene fugas. – Gman

Cuestiones relacionadas