¿Cómo detecta / evita pérdidas de memoria en su código (no administrado)? [cerrado]

125

En el código C / C ++ no administrado, ¿cuáles son las mejores prácticas para detectar pérdidas de memoria? ¿Y las pautas de codificación a evitar? (Como si fuera así de simple;)

En el pasado, hemos utilizado un poco tonto: tener un incremento de contador para cada llamada de asignación de memoria y decremento mientras se libera. Al final del programa, el valor del contador debe ser cero.

Sé que esta no es una buena manera y hay algunas capturas. (Por ejemplo, si está liberando memoria asignada por una llamada de API de plataforma, su recuento de asignación no coincidirá exactamente con su recuento de liberación. Por supuesto, incrementamos el contador al llamar a llamadas de API que asignaron memoria).

Espero sus experiencias, sugerencias y quizás algunas referencias a herramientas que simplifiquen esto.

prakash
fuente
En términos de evitar fugas, la siguiente publicación tiene algunos consejos: http://stackoverflow.com/questions/27492/c-memory-management
tonylo
Usé este con Visual Studio para detectar la fuga de memoria. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo
1
buscas valgrin (para linux) o deleaker (para windows), también miras el detector visual de fugas ...
John Smith
para encontrar pérdidas de memoria, consulte aquí: theunixshell.blogspot.com/2013/11/…
Vijay

Respuestas:

78

Si su código C / C ++ es portátil a * nix, pocas cosas son mejores que Valgrind .

Jordi Bunster
fuente
1
Valgrind ahora también funciona en OS X, por lo que Linux no es su única opción.
Michael Anderson
1
Valgrind para Linux (y OS X). Si usa windose, deleaker, ¡lo mejor de todo!
John Smith
@JordiBunster: ¡Qué bien! Pero basado en tiempo de ejecución. Con una base de código grande (escrita en C en mayúsculas y minúsculas) , principalmente probará su programa por la forma en que fue diseñado. Un atacante puede obtener los miles de horas necesarias para leer el código a fin de encontrar una vulnerabilidad de pérdida de memoria. Hubiera esperado una herramienta automatizada para el análisis del código fuente similar a la que existe para JavaScript.
user2284570
65

Si está utilizando Visual Studio, Microsoft proporciona algunas funciones útiles para detectar y depurar pérdidas de memoria.

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, incluya estos encabezados:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

Entonces debe llamar a esto cuando su programa salga:

_CrtDumpMemoryLeaks();

Alternativamente, si su programa no sale en el mismo lugar cada vez, puede llamar a esto al comienzo de su programa:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Ahora, cuando el programa salga, todas las asignaciones que no se liberaron se imprimirán en la Ventana de salida junto con el archivo en el que se asignaron y la aparición de la asignación.

Esta estrategia funciona para la mayoría de los programas. Sin embargo, se vuelve difícil o imposible en ciertos casos. El uso de bibliotecas de terceros que realizan algunas inicializaciones en el inicio puede hacer que aparezcan otros objetos en el volcado de memoria y puede dificultar el seguimiento de sus 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 de CRT causarán problemas.

Hay otras técnicas explicadas en el enlace de MSDN mencionadas anteriormente que también podrían usarse.

Dusty Campbell
fuente
Una nota sobre este método: parece que esto solo funciona si está utilizando C puro con malloc y gratis. El informe detallado que incluye números de línea no se crea si está utilizando los nuevos y eliminados de c ++.
Zach
2
@Zach: en realidad también puede hacer que esto funcione (para cualquier código que realmente compile usted mismo, de todos modos) - vea la respuesta aceptada en social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…
Roman Starkov
¿Funcionará también en modo de liberación?
JV
1
@ user3152463 No. Según la documentación, solo funcionará para la compilación de depuración: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Dusty Campbell
Esta línea es incorrecta: #define CRTDBG_MAP_ALLOC Debería ser: #define _CRTDBG_MAP_ALLOC
Fallso
37

En C ++: use RAII. Punteros inteligentes como std::unique_ptr, std::shared_ptr, std::weak_ptrson sus amigos.

Leon Timmermans
fuente
1
y std: vector es un gran reemplazo para cuando las matrices (buffers) se desasignan en la misma función que se asignan.
KJAWolf
44
Al menos std :: auto_ptr y boost :: shared_ptr siguen siendo susceptibles a fugas.
Jasper Bekkers
55
Solo si los usa incorrectamente, aunque debo admitir que para std :: auto_ptr usarlo incorrectamente es bastante fácil.
Leon Timmermans
2
Si bien este es un buen consejo para los estándares de codificación, no responde la pregunta. Incluso el uso de shared_ptr puede provocar fugas con dependencias circulares. Y puede tener "filtraciones" con estrategias de almacenamiento en caché ilimitadas, que se aplican incluso a los idiomas recolectados de basura.
CashCow
@CashCow: tienes razón. Si bien aún no lo he visto en la práctica, probablemente sea porque los estoy usando con moderación. Para citar la respuesta a continuación, "Use punteros solo cuando sea absolutamente necesario".
Leon Timmermans
28

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

  1. Use punteros solo cuando sea absolutamente necesario
  2. Si necesita un puntero, verifique dos veces si es posible un SmartPointer
  3. Utiliza el patrón GRASP Creator .

En cuanto a la detección de pérdidas de memoria personalmente, siempre he usado el Detector de fugas visual y considero que es muy útil.

Huppie
fuente
2
El Detector de fugas visuales se mudó al nuevo sitio vld.codeplex.com
KindDragon
VLD es un detector de fugas REALMENTE agradable - Lo recomiendo totalmente para todos los que usan VC ++
Javid
1
+1 para el punto 1. Esto es absolutamente lo fundamental. Desafortunadamente, me parece que algunas de las bibliotecas más grandes de "C ++" tienden a evitar la asignación de la pila y / o RAII a favor de los punteros en todas partes, a menudo sin razón aparente. Entonces, terminan siendo 'C con clases', no C ++ real.
underscore_d
16

He estado usando DevStudio durante demasiados años y siempre me sorprende cuántos programadores no saben acerca de 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 solicitudes de asignación de montón : específicamente la sección sobre números únicos de solicitud de asignación

_CrtSetDbgFlag

_CrtSetBreakAlloc

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

Skizz
fuente
10

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 los PDB de su versión de lanzamiento y configurar DebugDiag para usar el servidor de símbolos públicos de Microsoft)

Tal
fuente
3
El enlace ya no funciona, intente aquí para obtener documentación: support.microsoft.com/kb/2580960 y aquí para descargar: microsoft.com/en-us/download/details.aspx?id=26798
JPaget
7

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

Hernán
fuente
1
¡Esta herramienta es realmente perfecta! Le ahorra la molestia de usar _CrtDumpMemoryLeaks (); y amigos, como se describe en MSDN. ¡Solo uno incluye y expone todo! ¡Incluso funciona en viejas bibliotecas C!
m_pGladiator
La nueva versión (para VS2013) está aquí: vld.codeplex.com
Dženan
7

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

Si está utilizando C ++ siempre se puede evitar el uso de nuevo de forma explícita: usted tiene vector, string, auto_ptr(Pre C ++ 11; reemplazado por unique_ptren C ++ 11), unique_ptr(C ++ 11) y shared_ptr(C ++ 11) en su arsenal.

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

Serge
fuente
1
y no olvides la regla de 3 o de 5 entonces
Humam Helfawi
4

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

Paul Tomblin
fuente
4

Si está utilizando MS VC ++, le recomiendo 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 si hay fugas.

Una vez que haya compilado 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 pila de llamadas donde se generó cada fuga para buscar la línea de código ofensiva.

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

John Sibly
fuente
1
Verifiqué el buscador de fugas y parece estar bien, pero solo para tu información, no funcionará tal cual para x64 porque contiene un ensamblaje en línea.
Zach
3

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

Iain Holder
fuente
3

Si está utilizando Visual Studio, puede valer la pena mirar Bounds Checker . No es gratis, pero ha sido increíblemente útil para encontrar fugas en mi código. Tampoco solo pierde pérdidas de memoria, sino también pérdidas de recursos 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 filtración.

Herms
fuente
2

Creo que no hay una respuesta fácil a esta pregunta. La forma en que realmente puede abordar esta solución depende de sus requisitos. ¿Necesita una solución multiplataforma? ¿Estás usando new / delete o malloc / free (o ambos)? ¿Realmente está buscando solo "fugas" o desea una mejor protección, como la detección de desbordamientos (o desbordamientos) del búfer?

Si está trabajando en el lado de Windows, las bibliotecas de tiempo de ejecución de depuración de MS tienen alguna funcionalidad básica de detección de depuración y, como ya ha señalado otro, hay varios contenedores que pueden incluirse en su fuente para ayudar con la detección de fugas. Encontrar un paquete que pueda funcionar tanto con new / delete como malloc / free obviamente le brinda más flexibilidad.

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

Pero más allá de la detección de fugas, existe la noción de detectar daños en la memoria a través de desbordamientos (o desbordamientos) del búfer. Creo que este tipo de funcionalidad de depuración es más difícil que la detección de fugas simple. 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 diversas maneras, lo que provoca trucos para 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 nos pareció bastante desafiante.

marca
fuente
2

Me gustaría ofrecer algo que he usado en el pasado: un verificador 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 grosero, no dejo que eso me avergüence.

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

Hay cosas de las que debe tener cuidado al usarlo: no haga nada que deba apoyarse newen el código subyacente, tenga cuidado con las advertencias sobre los casos que podría fallar en la parte superior de leakcheck.cpp, tenga en cuenta que si activa en (y arregle cualquier problema con) el código que hace volcados de imágenes, puede generar un archivo enorme.

El diseño está diseñado para permitirle activar y desactivar el verificador sin volver a compilar todo lo que incluye su encabezado. Incluya leakcheck.h donde desee rastrear la comprobación y reconstruir una vez. A partir de entonces, compile leakcheck.cpp con o sin LEAKCHECK # definido y luego vuelva a vincularlo para encenderlo y apagarlo. Incluyendo unleakcheck.h lo desactivará localmente en un archivo. Se proporcionan dos macros: CLEARALLOCINFO () evitará informar el mismo archivo y línea de manera inapropiada cuando atraviese el código de asignación que no incluyó leakcheck.h. ALLOCFENCE () simplemente deja caer una línea en el informe generado sin hacer ninguna asignación.

Una vez más, tenga en cuenta que no he usado esto en mucho tiempo y es posible que tenga que trabajar un poco. Lo estoy dejando caer para ilustrar la idea. Si resulta que hay suficiente interés, estaría dispuesto a elaborar 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 de colores de sintaxis decente.

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

Thomas Kammeyer
fuente
2

Para Linux: pruebe Google Perftools

Hay muchas herramientas que hacen aloc / conteo libre similar, los pros de Goolge Perftools:

  • Bastante rápido (en comparación con valgrind: muy rápido)
  • Viene con una buena visualización gráfica de resultados
  • Tiene otras capacidades útiles: perfiles de CPU, perfiles de uso de memoria ...
Weidenrinde
fuente
2

La mejor defensa contra fugas es una estructura de programa que minimiza el uso de malloc. Esto no solo es bueno desde una perspectiva de programación, sino que también mejora el rendimiento y la mantenibilidad. No estoy hablando de usar otras cosas en lugar de malloc, sino en términos de reutilizar objetos y mantener pestañas muy explícitas sobre todos los objetos que se pasan, en lugar de asignar de cualquier manera como se acostumbra en idiomas con recolectores de basura. como Java

Por ejemplo, un programa en el que trabajo tiene un montón de objetos de cuadro que representan datos de imagen. Cada objeto de cuadro tiene datos secundarios, que el destructor del cuadro libera. El programa mantiene una lista de todos los marcos asignados, y cuando necesita uno nuevo, verifica una lista de objetos de marco no utilizados para ver si puede reutilizar uno existente en lugar de asignar uno nuevo. En el apagado, simplemente recorre la lista, liberando todo.

Shikari oscuro
fuente
2

Recomendaría usar Memory Validator de la verificación del software. Esta herramienta demostró ser de inestimable ayuda para ayudarme a rastrear pérdidas de memoria y mejorar la administración de memoria de las aplicaciones en las que estoy trabajando.

Una herramienta muy completa y rápida.

Fabien Hure
fuente
El Validador de memoria también proporciona el nombre de archivo y el número de línea para C # que llama a su código nativo. la versión x64 está en beta
Stephen Kellett
2

¿Está contando las asignaciones y liberaciones al interpolar sus propias funciones syscall que registran 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 que se originan 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 www.itworld.com.

Rob Wells
fuente
1

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

Escudo dan
fuente
1

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

Josh Matthews
fuente
1

Pauta general de codificación:

  • Los recursos se deben desasignar en la misma "capa" (función / clase / biblioteca) donde se asignan.
  • Si esto no es posible, intente utilizar una desasignación automática (aumentar el puntero compartido ...)
Weidenrinde
fuente
1

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 pérdidas de memoria / 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 para los recursos que desea asignar. Con este método es más difícil de "olvidar" y, en cierto sentido, obliga a pensar seriamente en el ciclo de vida de los recursos que se utilizan por adelantado en lugar de como un lado.

  2. Use el retorno lo más moderadamente posible. Lo que se asigna solo debe liberarse en un lugar si es posible. El camino condicional entre la adquisición de recursos y la liberación debe diseñarse para que sea lo más simple y obvio posible.

Einstein
fuente
1

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é sucede si acaba de notar que el sistema de producción tiene fugas en este momento y no tiene idea de cómo reproducirlo en la prueba? Alguna evidencia de lo que está mal se captura en el estado de ese sistema de producción, y podría ser suficiente para proporcionar una idea de dónde está el problema para que pueda reproducirlo.

Ahí es donde entra en escena el muestreo de Monte Carlo. Lea el artículo del blog de Raymond Chen, "La forma en que el pobre hombre identifica las pérdidas de memoria" y luego revise mi implementación (supone Linux, probado solo en x86 y x86-64)

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

tialaramex
fuente
1

Trabajando 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

aku
fuente
la férula es un reemplazo más nuevo para la pelusa.
Mark Kegel
@ user14788: El producto PC-Lint de Gimpel es mucho más moderno que el antiguo Unix lint. Tiene muchas comprobaciones específicas de C ++, que afaik splint no tiene. Vea el enlace en la respuesta (que renombré de Lint a PC-Lint).
Dan
0

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). El SDK de OS X también incluye una herramienta llamada MallocDebug (generalmente instalada en / Developer / Applications / Performance Tools /) que puede ayudarlo a monitorear el uso y las fugas.

jbl
fuente
0

Detectar:

CRT de depuración

Evitar:

Punteros inteligentes, boehm GC

DrPizza
fuente
0

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

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

quinmars
fuente
0

La mayoría de los perfiladores de memoria ralentizan mi gran aplicación compleja de Windows hasta el punto en 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

Sean
fuente
No veo por qué la desaceleración hace que los resultados sean inútiles. Seguramente la memoria que se pierde se pierde independientemente de la velocidad a la que se ejecuta el programa. El objetivo de estas herramientas es encontrar fugas, entonces, ¿dónde está el problema? ¿Funcionó tan lentamente que físicamente no pudo hacer que cubriera todas las rutas de código para perfilarlas?
underscore_d
-1

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

  1. configure la variable de entorno MALLOC_TRACE en bash
    MALLOC_TRACE = / tmp / mtrace.dat
    export MALLOC_TRACE;
  2. Agregue #include <mcheck.h> a la parte superior de su archivo fuente principal
  3. Añadir mtrace ();al comienzo de main y muntrace (); en la parte inferior (antes de la declaración de devolución)
  4. compile su programa con el modificador -g para obtener información de depuración
  5. ejecuta tu programa
  6. mostrar información de fuga con
    mtrace your_prog_exe_name /tmp/mtrace.dat
    ( primero tuve que instalar el script mtrace perl en mi sistema fedora con yum install glibc_utils   )
2 revoluciones
fuente
mtrace no es súper útil para C ++
Erin