2009-07-06 10 views
9

Tengo problemas con una pérdida lenta de memoria en mi aplicación de modo mixto C++/CLR .NET.Fuga de memoria en modo mixto Aplicación C++/CLR

(Es C++ bibliotecas estáticas nativos vinculados en una VS2008 C++/CLR Windows Forms aplicación con la configuración del compilador "/ CLR")

comportamiento típico: aplicación comienza a utilizar 30 MB de memoria (privado). Luego, la memoria pierde velocidad, digamos un MB por hora cuando se ejecuta bajo carga pesada simulada. Esto simula la aplicación en vivo por días o semanas.

He intentado utilizar varias herramientas para rastrear fugas de memoria, incluidas las herramientas de depuración CRT que vienen con las librerías Visual Studio CRT. También utilicé una herramienta comercial de detección de fugas ("Validador de memoria").

Ambas informan pérdidas de memoria insignificantes al apagar (unas pocas entradas menores que ascienden a unos pocos KB que no me preocupan). Además, puedo ver al ejecutar que la memoria rastreada no parece ser tan alta (así que no creo que sea solo la memoria la que se retenga y solo se libere al salir de la aplicación). Obtengo alrededor de 5 MB de memoria listada (de un total> 30MB).

La herramienta (Validador de memoria) está configurada para rastrear todo el uso de memoria (incluyendo malloc, nueva asignación de memoria virtual y muchos otros tipos de asignación de memoria). Básicamente, cada ajuste para el que se ha seleccionado la memoria para rastrear.

La imagen .NET informa que está utilizando alrededor de 1,5 MB de memoria (de perfmon).

Aquí hay una última información: tenemos una versión de la aplicación que se ejecuta como una aplicación de consola nativa (puramente nativa, no CLR en absoluto). Esto es 95% lo mismo que el modo mixto, excepto sin las cosas de UI. Esto no parece perder memoria en absoluto, y alcanza un máximo de alrededor de 5 MB de bytes privados.

Básicamente, lo que estoy tratando de transmitir aquí es que no creo que el código nativo tenga pérdidas de memoria.

Otra pieza del rompecabezas: He encontrado esto lo que se refiere a las pérdidas de memoria en aplicaciones en modo mixto cuando la orientación marco 2.0 (que soy): http://support.microsoft.com/kb/961870

Desafortunadamente los detalles son exasperantemente escasa así que no estoy seguro de si es relevante Intenté orientarme a 3.5 framework en lugar de a 2.0, pero aún tenía el mismo problema (tal vez no lo hice bien).

¿Alguien tiene alguna sugerencia?

Un par de cosas que me podrían ayudar:

  • ¿Hay algún otro tipo de asignaciones de memoria que no estoy de rastreo?
  • ¿Cómo es que las cifras no suman? Obtengo 5 MB de uso de memoria CRT, 1.5 MB de memoria .NET, ¿cómo es que toda la aplicación usa 30 MB de bytes privados? ¿Está todo eso atado en el framework .NET? ¿Por qué no los veo en la herramienta de fugas? ¿El marco .NET no aparecerá como algún tipo de memoria asignada?
  • ¿Alguna otra herramienta de detección de fugas que funcione bien con aplicaciones de modo mixto?

Gracias por cualquier ayuda

John

+0

.NET 2.0 y .NET 3.5 ambos usan el mismo CLR 2.0, como se puede ver fácilmente en los números de versión ;-) –

Respuesta

6

OK Finalmente encontré el problema.

Fue causado por una configuración incorrecta para/EH (Manejo de excepciones).

Básicamente, con las aplicaciones .NET de modo mixto, debe asegurarse de que todas las bibliotecas enlazadas estáticamente se compilan con/EHa en lugar de las EH predeterminadas.

(La propia aplicación también se debe compilar con/EHa, pero esto es un hecho -. El compilador informará de error si no lo usa El problema es cuando se vincula en otras librerías nativas estáticos.)

El problema es que las excepciones detectadas en el bit administrado de la aplicación, que se lanzaron dentro de las bibliotecas nativas compiladas con/EH, terminan por no manejar la excepción correctamente. Destructores para objetos C++ no se llaman correctamente.

En mi caso, esto solo ocurrió en un lugar raro, de ahí que me llevó años detectarlo.

0

Pruebe: DebugDiags.
Después de generar algunos volcados de memoria, le dará un buen resumen de qué memoria se asignó, y dependiendo de cómo encontrar su PDB, puede decirle por quién fue asignado.

+0

Gracias, voy a intentarlo – John

0

Puede tener una fuga de referencia, busque en el software de creación de perfiles ANTS. Ants Profiler

Una fuga de referencia es el equivalente en .net de una pérdida de memoria, tiene referencias a un objeto que detiene la recolección de elementos no utilizados, y así la memoria en uso comienza a aumentar.

+0

Pero no aparecería en el "# Bytes en todos los Heaps "valor de memoria .NET? He estado viendo esto en Process Explorer y no tiene fugas. La fuga está en bytes privados, pero no en "# bytes en todos los montones". – John

0

Es posible que haya pasado por alto algunos trituradores, puede suceder si usa GDI + y muchas otras API.

Si ejecuta la herramienta de análisis estático FXCop, tiene una regla para comprobar si ha llamado a disponer (o ha utilizado las instrucciones "using") en los objetos que proporcionan la interfaz. En .Net, si una función usa código no administrado, generalmente proporcionará un método de eliminación o cierre para que usted no filtre el recurso/memoria.

+0

Excelente idea. Supongo que estas filtraciones no aparecerían como uso de la memoria CLR porque son nativas, pero tampoco aparecerían en la herramienta nativa de detección de fugas porque no son mi código. Voy a dar un golpe (aunque no sé si FXCop puede manejar el modo mixto C++/CLI) – John

+0

espero que ayude. Solo estoy filmando en la oscuridad, pero he visto esto en una de las aplicaciones que hizo GDI, encontré que casi todos los objetos en GDI + necesitaban un triturador. Tenga en cuenta que esto fue antes del día de "usar" – Spence

4

Al igual que Spence estaba diciendo, pero para C++/CLI;) ....

Para cualquier objeto que se están utilizando en C++/CLI, si crea más de ese objeto a partir de código que C++, usted debe tratar para utilizar la semantica de asignacion de pila, aunque este es un tipo de cosa de compilacion magica, es capaz de configurar las sentencias __try {} __finally {} anidadas que puede estar acostumbrado a usar desde un codigo nativo (que es configurarlas de una manera que no perder una llamada a Dispose).

Nish'sarticle at the code project here en C++/CLI la semántica de la asignación de la pila es bastante buena y profundiza sobre cómo emular usando {}.

También debe asegurarse de eliminar de cualquier objeto que implment IDisposable como no se puede llamar a Dispose en C++/CLI, eliminar lo hace por usted, si no el uso de la semántica de la pila ..

por lo general llamada cercana a mí mismo en Streams y trato de asignar nullptr cuando termine con los objetos, por las dudas.

También es posible que desee comprobar hacia fuera this article on memory issues, perticularly sobre suscriptores de eventos, si está asignando el evento de que sus objetos, es posible que haya un escape ...

Como último recurso (o tal vez primero :), uno Lo que he hecho en el pasado es hacer uso de la API CLR profiler, here's another article sobre cómo hacer esto, el escritor del autor (Jay Hilyard) tiene un ejemplo que responde;

  • de cada tipo .NET que se utiliza, cómo muchas instancias de objetos están siendo asignan?
  • ¿Cuán grandes son las instancias de cada tipo?
  • ¿Qué notificaciones proporciona el GC a través de una recolección de basura y qué puede encontrar?
  • ¿Cuándo recoge el GC las instancias del objeto?

En caso de obtener una idea mejor que algunos de perfiles de productos básicos, me he dado cuenta de que pueden ser en ocasiones engañosa dependiendo de su porofile asignación (por cierto. Cuidado con los grandes temas objeto del montón,> ~ 83KB objetos están especialmente manipulados , en ese caso, lo recomiendo, al salir del gran montón de objetos :).

Teniendo en cuenta sus comentarios, algunas cosas más ...

He publicado antes sobre la carga de la imagen no está cargando cuota o cualquier otra estadística disernable, lo que esto significa, es posible que necesite para localizar a algún mango o el gestor problema (ver el bloqueo del cargador eventualmente), pero antes de eso, puedes intentar configurar un poco de Constrained Execution Regions, pueden hacer maravillas, pero también son desafortunadamente difíciles de adaptar en código no puro.

Este reciente MSDN Mag, documento del artículo es mucho perfmon memory sperlunking (seguimiento para this older one).

Desde VS Perf Blog, muestran cómo usar SOS en Visual Studio, que puede ser útil, para rastrear las DLL de Rouge, las publicaciones relacionadas también son buenas.

Maoni Stephen's Blog y company, él dice que está en el equipo de perf, pero esencialmente el 100% de sus mensajes son con respecto al GC tanto que puede que lo haya escrito.

Rick Byers es un desarrollador con el equipo de diagnóstico CLR, muchos de sus compañeros de blog-también son buenos de fuente, sin embargo, me gustaría sugerir fuertemente también refiriéndose a la bastante nuevo dev/diagnostics forum. Recientemente han ampliado el alcance de sus discusiones.

Code Coverage Tools y tracing a menudo puede ayudar, para darle una visión general de lo que realmente se está ejecutando.

(específicamente, las estadísticas perticuales pueden no darle una visión global de lo que está plauging su código, puedo decir que recientemente, he encontrado (incluso con .NET4beta binarios, el generador de perfiles de this company, es bastante bueno, es capaz de derivar filtraciones nativas/administradas de sus rastreos de perfil, lo regresa a las líneas de fuente exactas (incluso si está optimizado, es bastante bueno (y tiene una versión de prueba de 30 días)))).

¡Buena suerte! Espero que algo de esto me ayude, es algo nuevo en mi mente, ya que estoy haciendo mucho del mismo trabajo en este momento;)

+0

Gracias, algunos buenos comentarios aquí. Esto es lo siguiente: Ni los "bytes Total cometidos" ni las medidas del "montón de objetos grandes" en los objetos de rendimiento .NET parecen indicar una fuga en objetos .NET. Ahora estoy mucho más familiarizado con la depuración de fugas de memoria en código nativo que CLR, así que tal vez he entendido mal lo que significan estas cifras. ¿Podría corregirme sobre esto? ¿Aparecerán las pérdidas de memoria CLR en los objetos de rendimiento antes mencionados? – John