2011-04-13 14 views
36

Estoy usando el nuevo MemoryCache en .Net 4, con un límite máximo de tamaño de caché en MB (lo he probado entre 10 y 200 MB, en sistemas con entre 1,75 y 8GB de memoria). No establezco ninguna caducidad basada en el tiempo en los objetos, ya que estoy usando el caché simplemente como un disco de alto rendimiento, y mientras haya espacio, quiero que se use. Para mi sorpresa, el caché se negó a expulsar cualquier objeto, hasta el punto de que obtendría SystemOutOfMemory excepciones..Net 4 MemoryCache Leaks with Concurrent Garbage Collection

Me dispararon hasta perfmon, cableado mi solicitud a .Net CLR Memory\#Bytes In All Heaps, .Net Memory Cache 4.0 y Process\Private Bytes - de hecho, el consumo de memoria estaba fuera de control, y no hay recortes de caché se registraban.

hecho un poco de googling y stackoverflowing, descargar y adjunta la CLRProfiler, y zas : desalojos todas partes! La memoria se mantuvo dentro de límites razonables según el límite de tamaño de la memoria que había establecido. Lo ejecuté de nuevo en modo de depuración, sin desalojos. CLRProfiler nuevamente, desalojos.

Finalmente me di cuenta de que el generador de perfiles obligó a la aplicación a ejecutarse sin concurrent garbage collection (también ver útil SO Concurrent Garbage Collection Question). Lo apagué en mi app.config, y, efectivamente, ¡desalojos!

Este parece ser, en el mejor de una falta enorme de documentación para no decir: esto sólo funciona con la recolección de basura no concurrente- imagen que, aunque desde su portado desde ASP.NET, puede que no hayan tenido que preocuparse sobre la recolección de basura simultánea .

¿Alguien más ha visto esto? Me encantaría obtener algunas otras experiencias, y tal vez algunas ideas más educadas.


Actualización 1

he reproducido el problema dentro de un solo método: parece que la memoria caché debe estar escrito en paralelo para el desalojo de caché de prohibido hacer fuego (en el modo de recolección de desechos concurrente) Si hay algún interés, subiré el código de prueba a un repositorio público. Definitivamente me estoy haciendo hacia la parte más profunda de la CLR/GC/piscina MemoryCache, y creo que olvidé mis flotadores ...


Actualización 2

Me publicada test code on CodePlex para reproducir el problema. Además, posiblemente de interés, el código de producción original se ejecuta en Azure, como Rol de trabajador. Interesante, cambiar la configuración de concurrencia de GC en la aplicación app.config de la función no tiene ningún efecto. ¿Posiblemente Azure anula la configuración de GC como ASP.NET? Además, ejecutar el código de prueba en WPF frente a una aplicación de consola producirá resultados de desalojo ligeramente diferentes.

+6

llevar esto a connect.microsoft.com –

+0

Sin duda en mis tareas pendientes - de hecho, había una media llenado billete, pero luego no pudo reproducirlo en un solo método. Ahora que puedo, lo publicaré. –

+4

El problema de Microsoft Connect se archivó aquí: https://connect.microsoft.com/VisualStudio/feedback/details/661340/memorycache-evictions-do-not-fire-when-memory-limits-are-reached. –

Respuesta

8

Usted puede "forzar" una recolección de basura justo después del método problemática y ver si el problema se reproduce la ejecución:

System.Threading.Thread.Sleep(200); 
GC.Collect(); 
GC.WaitForPendingFinalizers(); 

justo al final del método (asegúrese de liberar cualquier asas para hacer referencia a objetos y anularlos). Si esto impide la pérdida de memoria, y luego sí, puede haber un error de tiempo de ejecución.

+1

Gran fragmento de código. Lo agregué a mi método de prueba y la "fuga" desaparece en cualquier configuración de recolección de basura. En mi código de producción, ahora sigo el "tamaño" de las entradas de caché, y llamo a este código de GC cuando el tamaño se ha vuelto más grande que las configuraciones de límite de tamaño de caché de memoria. Un poco de hack, pero parece funcionar. –

+0

En .NETFx> = 4.5.1 Ahora estoy haciendo lo siguiente en un temporizador (hilo de fondo) en una aplicación MVC GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); GC.WaitForPendingFinalizers(); –

+0

@ David Faivre: "cuando el tamaño se ha convertido en más grande que los ajustes de límite de tamaño MemoryCache" .. puede compartir un fragmento de código para esto? –

4

recolección de basura Stop-el-mundo se basa en la determinación de si existe una fuerte referencia a un objeto directo en el momento en que el mundo se detiene. La recolección concurrente de basura generalmente determina si una referencia en vivo fuerte a un objeto ha existido desde algún tiempo particular en el pasado. Mi conjetura sería que muchas referencias fuertes a los objetos contenidos en WeakReferences se crean y descartan individualmente. Si un recolector de basura que se detiene en el mundo dispara entre el momento en que se crea un objeto en particular y el momento en que se descarta, ese objeto en particular se mantendrá con vida, pero los objetos descartados anteriormente no se guardarán. Por el contrario, un recolector de basura simultáneo puede no detectar que todas las referencias sólidas de un objeto hayan sido descartadas hasta que pase una cierta cantidad de tiempo sin referencias fuertes a la creación de ese objeto.

A veces he deseado que .net ofrezca algo entre una referencia fuerte y una débil, lo que evitaría que un objeto se borre de la memoria, pero no lo protegería de que se finalizara o que tuviera WeakReferences débiles invalidados. . Dichas referencias complicarían ligeramente el proceso del GC, requiriendo que cada objeto tenga banderas separadas que indiquen si existen referencias fuertes y cuasi-débiles, y si se ha escaneado para referencias fuertes y cuasi débiles, pero tal característica podría ser útil. en muchos escenarios de "eventos débiles".

2

me encontré con esta entrada, mientras que la búsqueda de un tema similar y yo estoy centrado en su excepción de memoria insuficiente.

Si coloca un objeto en la memoria caché, puede hacer referencia a otros objetos y, por lo tanto, estos objetos no se recogerán. De ahí la excepción de memoria insuficiente y probablemente una CPU bloqueada debido a la recolección de basura Gen 2 .

están poniendo "usado" los objetos en la caché o clones de objetos "usados" en la memoria caché? Si coloca un clon en la memoria caché, el objeto "usado" que pueda hacer referencia a otros objetos podría ser basura.

Si cierra el mecanismo de caché funciona su programa todavía quede sin memoria? Si no se queda sin memoria, eso probaría que los objetos que de lo contrario estarías poniendo en la memoria caché todavía tienen referencias a otros objetos que dificultan la recolección de basura.

Forzar a la recolección de basura no es una buena práctica y no debería tener que hacer. En este escenario, forzar una recolección de basura no eliminaría los objetos referenciados de todos modos.

0

MemoryCache definitivamente tiene algunos problemas. Comió 160Mb de memoria en mi servidor asp.net, simplemente cambió a una lista simple y agregó un poco de lógica extra para obtener la misma funcionalidad.

Cuestiones relacionadas