2009-03-02 10 views
13

Se ha producido un problema espinoso con nuestra aplicación web aquí. (Asp.net 2.0 Win server 2008)Objeto no basura recolectada, pero no contiene gcroots

Nuestro uso de memoria para el sitio web, crece y crece a pesar de que yo esperaría que permanezca en un nivel bastante estático. (Tenemos una pequeña cantidad de datos que se almacenan en estado).

Queriendo saber cuál es el problema, he ejecutado un System.GC.Collect(); algunas veces, tomó un volcado de memoria y luego cargó este volcado de memoria en WinDbg.

Cuando hago un DumpHeap-Stat obtengo un número excesivamente grande en un tipo particular de memoria.

0000064280580b40 713471 79908752 PaymentOption

así, haciendo un dumpHeap -MT de este tipo, me sale un montón de referencias a objetos. Escogiendo un número aleatorio de estos, hago un! Gcroot y el comando regresa informando que no se mantienen referencias a él.

Para mí, esto es exactamente cuando el GC debe recoger estos elementos, pero por alguna razón se han dejado pendientes.

¿Alguien puede dar una explicación de lo que podría estar pasando?

+0

Estaría interesado en ver si alguna vez resolvió esto ... – womp

+1

Desafortunadamente no. Llegamos a una solución al reciclar el proceso una vez que utilizó más de nGBs de memoria, por lo que permitió otros procesos. Sospecho que es un problema con la fragmentación y la nueva asignación de memoria. Voy a investigar un poco más pronto, así que publicaré una actualización. – Lachmania

Respuesta

1

No sin más información sobre su aplicación. Pero nos encontramos con algunos problemas de memoria desagradables hace mucho tiempo. ¿Utiliza el almacenamiento en caché de ASP.NET? Como le gusta decir a Raymond Chen, "una mala estrategia de almacenamiento en caché es indistinguible de una fuga de memoria".

Echa un vistazo a otra herramienta - CLRProfiler.exe - te ayudará a atravesar árboles de referencia de objetos para ver dónde están enraizados tus objetos. Esto también es bueno: link text

Has oído esto antes, si tienes que recoger GC, algo está mal.

+0

No hay almacenamiento en caché, que es lo más extraño que está pasando aquí. Tenemos un componente de informes de terceros que por algún motivo usa el caché (se puede saber por los gcroots que tiene) – Lachmania

+0

Gracias por la sugerencia del CLRProfiler, pero el problema es que cuando se forma un TraverseHeap y se escribe el xml, los objetos que me interesan no están allí. (Esto es lo que esperaría, ya que parece que no tienen ninguna raíz válida.) – Lachmania

+0

Para ser claros, tengo un botón que llama a GC.Recolecta eso presiono un par de veces antes de tomar un volcado de memoria, no recibe llamadas durante el funcionamiento normal. – Lachmania

1

¿El objeto PaymentOption se crea en un proceso asincrónico, por casualidad? Recuerdo algo, si no llamas a EndInvoke, puedes tener problemas como este.

2

Pocas cosas:

  1. GC.Collect no le ayudará a hacer ninguna depuración. El recolector de basura ya se está llamando: si hubiera objetos disponibles para su recolección, ya habría sucedido.
  2. La memoria inactiva en un servidor es una memoria desperdiciada. ¿Estás seguro de que la memoria se está "filtrando", o es solo que el marco está decidiendo que puede mantener más cosas en memoria o mantener más memoria para un acceso más rápido? En este caso, sospecho que está perdiendo memoria, pero es algo que debe verificarse.
  3. Parece que algo que no espera es mantener una referencia a los objetos PaymentOption. Tal vez una colección estática en alguna parte? O hilo separado?
+1

pt2: Estoy de acuerdo en que la memoria inactiva se desperdicia, pero estoy viendo gigas de ram que se usan en objetos que no están rooteados, pero que parecen no ser recopilados. – Lachmania

+1

pt3: sospecho que eso es lo que está pasando, pero con! Gcroot no informando ninguna referencia, no estoy seguro de dónde encontrar lo que puede estar reteniéndolos. Supongo que ese es el punto de mi pregunta. – Lachmania

+0

Sé que esto es viejo ahora, pero quería agregar algo que se me ocurrió y no se menciona en ningún otro lugar en la pregunta. Este podría ser el gran montón de objetos. Si existe la posibilidad de que estos objetos tengan> 80,000 bytes, podría tener un problema con esto. –

4

Puede intentar usar sosex.dll en Windbg, que es una extensión escrita para ayudar con la depuración de .NET. Hay un comando llamado! Refs que es similar a! Gcroot, en el sentido de que le mostrará todos los objetos que hacen referencia a un objeto, además de que mostrará todos los objetos a los que hace referencia.

En el ejemplo del sitio web del autor,!árbitros se usa contra un objeto y la salida tiene el siguiente aspecto:

0:000> !refs 0000000080000db8 
Objects referenced by 0000000080000db8 (System.Threading.Mutex): 
0000000080000ef0   32 Microsoft.Win32.SafeHandles.SafeWaitHandle 

Objects referencing 0000000080000db8 (System.Threading.Mutex): 
0000000080000e08   72 System.Threading.Mutex+<>c__DisplayClass3 
0000000080000e50   64 System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode 
+0

Gracias por el consejo. Pero ya lo he comprobado y no me ha gustado el comando! Refs. No informó raíces. grrr – Lachmania

1

he estado investigando el mismo tema a mí mismo y se preguntan por qué los objetos que tenían no estaban siendo recogidos no hay referencias.

Los objetos de más de 85,000 bytes se almacenan en el Gran montón de objetos, de los cuales la memoria se libera con menos frecuencia.

http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

Una sola PaymentOption puede no ser tan grande, pero ¿están contenidas dentro de las colecciones, o se basan en algo así como un conjunto de datos? Debe elegir algunas instancias de PaymentOption/collection/DataSet y luego usar el comando sos! Objsize para ver lo grandes que son.

Lamentablemente, esto realmente no responde la pregunta. Me gusta pensar que puedo confiar en .NET Framework para que se encargue de liberar la memoria no utilizada siempre que sea necesario. Sin embargo, veo que el proceso de trabajo que ejecuta la aplicación que estoy viendo usa mucha memoria, incluso cuando la memoria se ve bastante apretada en el servidor.

2

¿PaymentObject implementa un finalizador por casualidad? ¿Llama a un objeto STA COM?

Me gustaría ver la salida de! Finalizequeue para ver si el recuento de objetos que se muestran en el montón es aproximadamente la cantidad de los que pueden estar esperando a finalizar. Salida probablemente debería ser algo como esto:

generation 0 has 57 finalizable objects (0409b5cc->0409b6b0) 
generation 1 has 55 finalizable objects (0409b4f0->0409b5cc) 
generation 2 has 0 finalizable objects (0409b4f0->0409b4f0) 
Ready for finalization 0 objects (0409b6b0->0409b6b0) 

Si el número de listo para su finalización objetos sigue creciendo, y sus ciertas colecciones de basura están ocurriendo (confirmar a través de los contadores de monitor de rendimiento), entonces podría ser un finalizador bloqueado hilo. Es posible que necesite tomar varias instantáneas durante la vida del proceso (antes de un reciclaje) para confirmar. Por lo general, confío en el número mágico de tres, siempre y cuando el sitio esté bajo algún tipo de carga.

Un error en un finalizador puede bloquear el hilo del finalizador y evitar que los objetos se recopilen alguna vez.

Si el objeto PaymentOption llama a un objeto COM heredado de STA, entonces este artículo ASP.NET Hang and OutOfMemory exceptions caused by STA components podría apuntar en la dirección correcta.

0

FYI, SOS en .NET 4 es compatible con algunos nuevos comandos que pueden ser de ayuda, es decir !gcwhere (localice la generación de una objeción; gcgen de sosex) y !findroots (hace lo que dice en la lata;! De sosex árbitros)

Ambos están documentados en SOS documentation y mentioned on Tess Ferrandez's blog.

Cuestiones relacionadas