¿Cómo se detectan / evitan memory leaks en su código (no administrado)?

En el código C / C ++ no administrado, ¿cuáles son las mejores prácticas para detectar memory leaks? Y las pautas de encoding para evitar? (Como si fuera así de simple;)

Hemos utilizado un poco de una manera tonta en el pasado: tener un incremento de contador para cada llamada de asignación de memoria y decremento mientras se libera. Al final del progtwig, el valor del contador debe ser cero.

Sé que esta no es una gran manera y hay algunas capturas. (Por ejemplo, si está liberando memoria que fue asignada por una llamada API de la plataforma, su recuento de asignación no coincidirá exactamente con su conteo de liberación. Por supuesto, luego incrementamos el contador cuando llamamos a la API llama a la memoria asignada).

Espero sus experiencias, sugerencias y tal vez algunas referencias a herramientas que simplifiquen esto.

Si su código C / C ++ es portable a * nix, algunas cosas son mejores que Valgrind .

Si está utilizando Visual Studio, Microsoft proporciona algunas funciones útiles para detectar y depurar memory leaks.

Comenzaría con este artículo: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

Aquí está el resumen rápido de esos artículos. Primero, incluye estos encabezados:

#define _CRTDBG_MAP_ALLOC #include  #include  

Luego debe llamar a esto cuando su progtwig finalice:

 _CrtDumpMemoryLeaks(); 

Alternativamente, si su progtwig no sale en el mismo lugar cada vez, puede llamarlo al comienzo de su progtwig:

 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 

Ahora, cuando el progtwig finalice, todas las asignaciones que no estaban libres se imprimirán en la ventana de salida junto con el archivo en el que fueron asignadas y la ocurrencia de la asignación.

Esta estrategia funciona para la mayoría de los progtwigs. Sin embargo, se vuelve difícil o imposible en ciertos casos. El uso de bibliotecas de terceros que inicien durante el inicio puede hacer que aparezcan otros objetos en el volcado de memoria y dificultar el rastreo de las fugas. Además, si alguna de sus clases tiene miembros con el mismo nombre que cualquiera de las rutinas de asignación de memoria (como malloc), las macros de depuración CRT causarán problemas.

Hay otras técnicas explicadas en el enlace de MSDN al que se hizo referencia anteriormente que también se podrían usar.

En C ++: use RAII. Punteros inteligentes como std :: unique_ptr, std :: shared_ptr, std :: weak_ptr son tus amigos.

Como desarrollador de C ++ aquí hay algunas pautas simples:

  1. Use punteros solo cuando sea absolutamente necesario
  2. Si necesita un puntero, haga una doble comprobación si es posible un SmartPointer
  3. Use el patrón GRASP Creator .

En cuanto a la detección de pérdidas de memoria personalmente, siempre he usado Visual Leak Detector y creo que es muy útil.

Llevo muchos años usando DevStudio y siempre me sorprende cuántos progtwigdores no conocen las herramientas de análisis de memoria que están disponibles en las bibliotecas de tiempo de ejecución de depuración. Aquí hay algunos enlaces para comenzar:

Seguimiento de las Solicitudes de Asignación de Heces : específicamente la sección de Números de Solicitud de Asignación Única

_CrtSetDbgFlag

_CrtSetBreakAlloc

Por supuesto, si no está usando DevStudio, esto no será particularmente útil.

Me sorprende que nadie haya mencionado DebugDiag para el sistema operativo Windows.
Funciona en versiones de lanzamiento, e incluso en el sitio del cliente.
(Solo necesita mantener su versión de lanzamiento PDB y configurar DebugDiag para usar el servidor de símbolo público de Microsoft)

Visual Leak Detector es una herramienta muy buena, aunque no admite las llamadas en los tiempos de ejecución de VC9 (por ejemplo, MSVCR90D.DLL).

Microsoft VC ++ en modo de depuración muestra pérdidas de memoria, aunque no muestra dónde están las fugas.

Si está usando C ++, siempre puede evitar el uso de nuevos explícitamente: tiene vector , string , auto_ptr (pre C ++ 11; reemplazado por unique_ptr en C ++ 11), unique_ptr (C ++ 11) y shared_ptr (C ++ 11) en tu arsenal.

Cuando lo nuevo es inevitable, intente ocultarlo en un constructor (y ocultarlo en un destructor); lo mismo funciona para API de terceros.

Hay varias bibliotecas de reemplazo “malloc” que le permitirán llamar a una función al final y le informará sobre toda la memoria no actualizada, y en muchos casos, quién la colocó mal (o la cambió) en primer lugar. .

Si está utilizando MS VC ++, puedo recomendar encarecidamente esta herramienta gratuita del proyecto de código: leakfinder de Jochen Kalmbach.

Simplemente agregue la clase a su proyecto y llame

 InitAllocCheck(ACOutput_XML) DeInitAllocCheck() 

antes y después del código que desea verificar para detectar fugas.

Una vez que haya comstackdo y ejecutado el código, Jochen proporciona una herramienta GUI ordenada donde puede cargar el archivo .xmlleaks resultante y navegar a través de la stack de llamadas donde se generó cada fuga para buscar la línea ofensiva de código.

El PurifyPlus de Rational (ahora propiedad de IBM) ilustra las fugas de una manera similar, pero considero que la herramienta de detección de fugas en realidad es más fácil de usar, ¡con la ventaja de que no cuesta varios miles de dólares!

Nunca lo usé yo mismo, pero mis amigos C me dicen Purify .

Si está usando Visual Studio podría valer la pena mirar a Bounds Checker . No es gratis, pero ha sido increíblemente útil para encontrar fugas en mi código. No solo hace memory leaks, sino también fugas de recursos de GDI, errores de uso de WinAPI y otras cosas. Incluso le mostrará dónde se inicializó la memoria filtrada, por lo que es mucho más fácil rastrear la fuga.

Creo que no hay una respuesta fácil para esta pregunta. Cómo puede realmente acercarse a esta solución depende de sus requisitos. ¿Necesitas una solución multiplataforma? ¿Estás usando new / delete o malloc / free (o ambos)? ¿Realmente está buscando simplemente “fugas” o quiere una mejor protección, como la detección de desbordamientos del búfer (o infrautilizaciones)?

Si está trabajando en el lado de Windows, las bibliotecas de tiempo de ejecución de depuración de MS tienen algunas funciones básicas de detección de depuración, y como ya se ha señalado, hay varios envoltorios que se pueden incluir en su fuente para ayudar con la detección de fugas. Encontrar un paquete que pueda funcionar con new / delete y malloc / free obviamente le da más flexibilidad.

No sé lo suficiente sobre el lado de Unix para brindar ayuda, aunque nuevamente, otros sí.

Pero más allá de la detección de fugas, existe la noción de detectar la corrupción de la memoria a través de los desbordamientos (o subutilizaciones) del búfer. Este tipo de funcionalidad de depuración es más difícil que la simple detección de fugas. Este tipo de sistema también se complica aún más si está trabajando con objetos C ++ porque las clases polimórficas se pueden eliminar de diferentes maneras, causando problemas al determinar el puntero base verdadero que se está eliminando. No conozco ningún buen sistema “gratuito” que ofrezca una protección decente para los excesos. hemos escrito un sistema (multiplataforma) y lo encontramos bastante desafiante.

Me gustaría ofrecer algo que he usado en el pasado: un comprobador de fugas rudimentario que es de nivel fuente y bastante automático. Estoy regalando esto por tres razones:

  1. Tu podrias encontrar esto útil.

  2. Aunque es un poco krufty, no dejo que eso me avergüence.

  3. Aunque está vinculado a algunos ganchos win32, eso debería ser fácil de aliviar.

Hay cosas de las que debe tener cuidado al usarlas: no haga nada que necesite apoyarse en las new en el código subyacente, tenga cuidado con las advertencias sobre casos que podría pasar por alto en la parte superior de leakcheck.cpp, tenga en cuenta que si activar (y corregir cualquier problema) el código que hace los volcados de imágenes, puede generar un archivo enorme.

El diseño está destinado a permitirle activar y desactivar el comprobador sin volver a comstackr todo lo que incluye su encabezado. Incluya leakcheck.h donde quiera rastrear cheques y reconstruir una vez. A partir de entonces, compile leakcheck.cpp con o sin LEAKCHECK # defined’d y vuelva a vincular para activarlo y desactivarlo. Incluir unleakcheck.h lo apagará localmente en un archivo. Se proporcionan dos macros: CLEARALLOCINFO () evitará informar el mismo archivo y la misma línea de forma inapropiada cuando recorra la asignación del código que no incluyó leakcheck.h. ALLOCFENCE () simplemente coloca una línea en el informe generado sin hacer ninguna asignación.

De nuevo, por favor, dense cuenta de que no he usado esto desde hace tiempo y es posible que tengan que trabajar un poco. Lo dejo caer para ilustrar la idea. Si resulta que hay suficiente interés, estaría dispuesto a buscar un ejemplo, actualizar el código en el proceso y reemplazar el contenido de la siguiente URL con algo más agradable que incluya una lista decentemente syntax.

Puede encontrarlo aquí: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

Para Linux: prueba Google Perftools

Hay muchas herramientas que hacen un conteo libre de alloc / free, los pros de Goolge Perftools:

  • Muy rápido (en comparación con valgrind: muy rápido)
  • Viene con una buena pantalla gráfica de resultados
  • Tiene otras capacidades útiles: perfil de CPU, perfil de uso de memoria …

La mejor defensa contra fugas es una estructura de progtwig que minimiza el uso de malloc. Esto no solo es bueno desde una perspectiva de progtwigción, sino que también mejora el rendimiento y la capacidad de mantenimiento. No estoy hablando de usar otras cosas en lugar de malloc, sino en términos de volver a usar objetos y mantener tabs muy explícitas sobre todos los objetos que circulan en lugar de asignarlos de cualquier manera como a menudo se acostumbra en los lenguajes con recolectores de basura como Java.

Por ejemplo, un progtwig en el que trabajo tiene un grupo de objetos de marco que representan datos de imagen. Cada objeto de cuadro tiene subdatos, que el destructor del marco libera. El progtwig mantiene una lista de todos los marcos que se asignan, y cuando necesita uno nuevo, comprueba una lista de objetos de marco no utilizados para ver si puede reutilizar uno existente en lugar de asignar uno nuevo. Al apagarlo, simplemente itera por la lista, liberando todo.

Recomendaría utilizar el Validador de memoria de verificación de software. Esta herramienta demostró ser de gran ayuda para ayudarme a rastrear las pérdidas de memoria y mejorar la administración de la memoria de las aplicaciones en las que estoy trabajando.

Una herramienta muy completa y rápida.

¿Está contando los allocs y libera interpolando sus propias funciones syscall que graban las llamadas y luego pasan la llamada a la función real?

Esta es la única forma en que puede realizar un seguimiento de las llamadas originadas en el código que no ha escrito.

Echa un vistazo a la página de manual de ld.so. O ld.so.1 en algunos sistemas.

También haga Google LD_PRELOAD y encontrará algunos artículos interesantes que explican la técnica en http://www.itworld.com.

Al menos para MS VC ++, la biblioteca C Runtime tiene varias funciones que he encontrado útiles en el pasado. Compruebe la ayuda de MSDN para las funciones _Crt* .

El mmgr de Paul Nettle es una herramienta favorita desde hace mucho tiempo. Incluye mmgr.h en sus archivos de origen, define TEST_MEMORY y entrega un archivo de texto lleno de problemas de memoria que ocurrieron durante la ejecución de su aplicación.

Directriz de encoding general:

  • Los recursos deben ser desasignados en la misma “capa” (función / clase / biblioteca) donde se asignan.
  • Si esto no es posible, intente usar una desasignación automática (impulsar el puntero compartido …)

Las herramientas de depuración de memoria valen su peso en oro, pero a lo largo de los años he descubierto que se pueden usar dos ideas simples para evitar que la mayoría de las memory leaks / recursos se codifiquen en primer lugar.

  1. Escriba el código de liberación inmediatamente después de escribir el código de adquisición de los recursos que desea asignar. Con este método, es más difícil “olvidar” y, en cierto sentido, obliga a uno a pensar seriamente en el ciclo de vida de los recursos utilizados por adelantado en lugar de hacerlo a un lado.

  2. Use return tan sparringly como sea posible. Lo que se asigna solo se debe liberar en un lugar si es posible. La ruta condicional entre la adquisición del recurso y la versión debe diseñarse para que sea lo más simple y obvia posible.

En la parte superior de esta lista (cuando lo leí) estaba valgrind. Valgrind es excelente si puede reproducir la fuga en un sistema de prueba. Lo he usado con gran éxito.

¿Qué pasa si acabas de notar que el sistema de producción está goteando en este momento y no tienes idea de cómo reproducirlo en la prueba? Se captura cierta evidencia de lo que está mal en el estado de ese sistema de producción, y puede ser suficiente para proporcionar una idea de dónde está el problema para que pueda reproducirlo.

Ahí es donde el muestreo de Monte Carlo entra en escena. Lea el artículo del blog de Raymond Chen, “La manera del pobre de identificar filtraciones de memoria” y luego revise mi implementación (se supone Linux, probado solo en x86 y x86-64)

http://github.com/tialaramex/leakdice/tree/master

Al trabajar en el sistema operativo de teléfonos celulares Motorola, secuestramos la biblioteca de asignación de memoria para observar todas las asignaciones de memoria. Ayudó a encontrar muchos problemas con las asignaciones de memoria. Como la prevención es mejor que el curado, recomendaría usar una herramienta de análisis estático como Klockwork o PC-Lint.

Valgrind es una buena opción para Linux. En MacOS X, puede habilitar la biblioteca MallocDebug que tiene varias opciones para depurar problemas de asignación de memoria (consulte la página de manual de malloc, la sección “MEDIO AMBIENTE” tiene los detalles relevantes). OS X SDK también incluye una herramienta llamada MallocDebug (generalmente instalada en / Developer / Applications / Performance Tools /) que puede ayudarlo a controlar el uso y las fugas.

Detectar:

Depurar CRT

Evitar:

Punteros inteligentes, boehm GC

Un buen reemplazo de malloc, calloc y reallloc es rmdebug, es bastante simple de usar. Es mucho más rápido valgrind entonces puede probar su código extensivamente. Por supuesto, tiene algunas desventajas, una vez que encuentre una fuga probablemente necesite usar valgrind para encontrar dónde aparece la fuga y solo puede probar mallocs que hace directamente. Si una lib pierde porque la usas mal, rmdebug no la encontrará.

http://www.hexco.de/rmdebug/

La mayoría de los perfiladores de memoria ralentizan mi gran aplicación compleja de Windows hasta el punto de que los resultados son inútiles. Hay una herramienta que funciona bien para encontrar fugas en mi aplicación: UMDH – http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx

Mtrace parece ser el estándar incorporado para Linux. Los pasos son:

  1. configurar la variable de entorno MALLOC_TRACE en bash
    MALLOC_TRACE = / tmp / mtrace.dat
    exportar MALLOC_TRACE;
  2. Agregue #include a la parte superior de su archivo fuente principal
  3. Agregar mtrace (); al comienzo de main y muntrace (); en la parte inferior (antes de la statement de devolución)
  4. comstack tu progtwig con el modificador -g para la información de depuración
  5. ejecuta tu progtwig
  6. mostrar información de fuga con
    mtrace your_prog_exe_name /tmp/mtrace.dat
    (Tuve que instalar el script mtrace perl primero en mi sistema fedora con yum install glibc_utils )