¿Cómo encontrar la pérdida de memoria en un código / proyecto de C ++?

Soy un progtwigdor de C ++ en la plataforma de Windows. Estoy usando Visual Studio 2008.

Por lo general, termino en el código con pérdidas de memoria.

Normalmente encuentro la fuga de memoria inspeccionando el código, pero es engorroso y no siempre es un buen enfoque.

Como no puedo pagar una herramienta de detección de memory leaks pagada, quería que sugirieran la mejor manera de evitar memory leaks.

  1. Quiero saber cómo el progtwigdor puede encontrar pérdidas de memoria.
  2. ¿Hay algún estándar o procedimiento que se debe seguir para garantizar que no haya pérdida de memoria en el progtwig?

Instrucciones

Cosas que necesitarás

  • Competencia en C ++
  • Comstackdor C ++
  • Depurador y otras herramientas de software de investigación

1

Comprende los principios básicos del operador. El operador C ++ “nuevo” asigna memoria de stack. El operador “eliminar” libera la memoria del montón. Por cada “nuevo”, debe usar un “eliminar” para liberar la misma memoria que asignó:

char* str = new char [30]; // Allocate 30 bytes to house a string. delete [] str; // Clear those 30 bytes and make str point nowhere. 

2

Reasigna memoria solo si has eliminado. En el siguiente código, str adquiere una nueva dirección con la segunda asignación. La primera dirección se pierde irremediablemente, y también lo hacen los 30 bytes a los que apuntaba. Ahora son imposibles de liberar y tienes una pérdida de memoria:

 char* str = new char [30]; // Give str a memory address. // delete [] str; // Remove the first comment marking in this line to correct. str = new char [60]; /* Give str another memory address with the first one gone forever.*/ delete [] str; // This deletes the 60 bytes, not just the first 30. 

3

Mira esas asignaciones de puntero. Cada variable dinámica (memoria asignada en el montón) necesita asociarse con un puntero. Cuando una variable dinámica se desasocia de su (s) puntero (s), se vuelve imposible de borrar. De nuevo, esto da como resultado una pérdida de memoria:

 char* str1 = new char [30]; char* str2 = new char [40]; strcpy(str1, "Memory leak"); str2 = str1; // Bad! Now the 40 bytes are impossible to free. delete [] str2; // This deletes the 30 bytes. delete [] str1; // Possible access violation. What a disaster! 

4

Tenga cuidado con los indicadores locales. Un puntero que declara en una función se asigna en la stack, pero la variable dinámica a la que apunta se asigna en el montón. Si no lo elimina, persistirá después de que el progtwig salga de la función:

 void Leak(int x){ char* p = new char [x]; // delete [] p; // Remove the first comment marking to correct. } 

5

Preste atención a los corchetes cuadrados después de “eliminar”. Use “eliminar” solo para liberar un solo objeto. Utilice “eliminar” [] con corchetes para liberar una matriz de montón. No hagas algo como esto:

 char* one = new char; delete [] one; // Wrong char* many = new char [30]; delete many; // Wrong! 

6

Si la filtración aún está permitida, normalmente la busco con un delegado (verifíquela aquí: http://deleaker.com ).

¡Gracias!

Puede usar algunas técnicas en su código para detectar memory leaks. La forma más común y más fácil de detectar es definir un macro decir, DEBUG_NEW y usarlo, junto con macros predefinidos como __FILE__ y __LINE__ para localizar la pérdida de memoria en su código. Estas macros predefinidas le indican el archivo y el número de línea de pérdidas de memoria.

DEBUG_NEW es solo un MACRO que generalmente se define como:

 #define DEBUG_NEW new(__FILE__, __LINE__) #define new DEBUG_NEW 

De modo que siempre que utilice algo new , también pueda realizar un seguimiento del número de archivo y línea que podría usarse para localizar la pérdida de memoria en su progtwig.

¡Y __FILE__ , __LINE__ son macros predefinidos que evalúan el nombre de archivo y el número de línea, respectivamente, donde los usa!

Lea el siguiente artículo que explica la técnica de usar DEBUG_NEW con otras macros interesantes, muy bellamente:

Un detector de fuga de memoria multiplataforma


De Wikpedia ,

Debug_new se refiere a una técnica en C ++ para sobrecargar y / o redefinir el operador new y el operador delete para interceptar la asignación de memoria y las llamadas de desasignación, y así depurar un progtwig para el uso de la memoria. A menudo implica definir una macro llamada DEBUG_NEW, y hace que new se convierta en algo nuevo (_ FILE _, _ LINE _) para registrar la información de archivo / línea en la asignación. Microsoft Visual C ++ utiliza esta técnica en sus Microsoft Foundation Classes. Hay algunas maneras de extender este método para evitar el uso de la redefinición de macros mientras todavía se puede mostrar la información de archivo / línea en algunas plataformas. Hay muchas limitaciones inherentes a este método. Se aplica solo a C ++, y no puede detectar memory leaks mediante funciones C como malloc. Sin embargo, puede ser muy simple de usar y también muy rápido, en comparación con algunas soluciones de depuración de memoria más completas.

Existen algunas técnicas de progtwigción bien conocidas que lo ayudarán a minimizar el riesgo de tener memory leaks de primera mano:

  • si tiene que hacer su propia asignación de memoria dinámica, escriba new y delete siempre en pares, y asegúrese de que el código de asignación / desasignación se llame pairwise
  • evite la asignación de memoria dinámica si puede. Por ejemplo, use el vector t donde sea posible en lugar de T* t = new T[size]
  • utilice “punteros inteligentes” como potenciar punteros inteligentes ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • Mi favorito personal: asegúrese de haber entendido el concepto de propiedad de un puntero y asegúrese de que en todos los lugares donde usa punteros, sepa qué entidad de código es el propietario.
  • aprende qué constructores / operadores de asignación son creados automáticamente por el comstackdor C ++, y qué significa eso si tienes una clase que posee un puntero (o lo que eso significa si tienes una clase que contiene un puntero a un objeto que no posee).
  1. Descargue Herramientas de depuración para Windows .
  2. Use la utilidad gflags para activar los gflags stack de modo de usuario.
  3. Use UMDH para tomar varias instantáneas de la memoria de su progtwig. Tome una instantánea antes de asignar la memoria, y tome una segunda instantánea después de un punto en el que cree que su progtwig ha filtrado la memoria. Es posible que desee agregar pausas o indicaciones en su progtwig para darle la oportunidad de ejecutar UMDH y tomar las instantáneas.
  4. Ejecute UMDH nuevamente, esta vez en su modo que hace una diferencia entre las dos instantáneas. A continuación, generará un informe que contiene las stacks de llamadas de pérdidas de memoria sospechosas.
  5. Restaure su configuración de gflags anterior cuando haya terminado.

UMDH le dará más información que el montón de depuración de CRT porque está observando las asignaciones de memoria en todo el proceso; incluso puede decirle si los componentes de terceros tienen fugas.

Si usas gcc, hay gprof disponible.

Quería saber cómo el progtwigdor encuentra la pérdida de memoria

Algunas usan herramientas, otras hacen lo que tú haces, también podrían hacerlo a través de la revisión del código de pares

¿Hay algún estándar o procedimiento que se debe seguir para garantizar que no haya pérdida de memoria en el progtwig?

Para mí: cada vez que creo objetos asignados dinámicamente, siempre pongo el código de liberación después, luego llene el código entre. Esto estaría bien si está seguro de que no habrá excepciones en el código entre. De lo contrario, hago uso de try-finally (no uso C ++ con frecuencia).

  1. En Visual Studio, hay un detector incorporado para la pérdida de memoria llamado C Runtime Library. Cuando su progtwig finalice después de que la función principal regrese, CRT verificará el montón de depuración de su aplicación. si aún tiene asignados algunos bloques en el montón de depuración, entonces tiene una pérdida de memoria.

  2. Este foro analiza algunas formas de evitar la pérdida de memoria en C / C ++.

Busque en su código las apariciones de new y asegúrese de que todas ocurran dentro de un constructor con una eliminación correspondiente en un destructor. Asegúrese de que esta sea la única operación de lanzamiento posible en ese constructor. Una forma simple de hacerlo es ajustar todos los punteros en std::auto_ptr , o boost::scoped_ptr (dependiendo de si necesita semántica de movimiento). Para todos los códigos futuros, solo asegúrese de que cada recurso sea propiedad de un objeto que limpie el recurso en su destructor. Si necesita semántica de movimiento, puede actualizar a un comstackdor que admita referencias de valor r (creo que VS2010) y crear constructores de movimiento. Si no quieres hacer eso, entonces puedes utilizar una variedad de técnicas complicadas que implican un uso concienzudo del intercambio, o prueba la biblioteca Boost.Move.

Ejecutar “Valgrind” puede:

1) Ayuda a identificar memory leaks : le muestra cuántas memory leaks tiene y señala las líneas en el código donde se asignó la memoria filtrada.

2) Señale los bashs incorrectos de liberar memoria (por ejemplo, llamada impropia de “eliminar”)

Instrucciones para usar “Valgrind”

1) Obtenga valgrind aquí .

1) Comstack tu código con la bandera -g

3) En tu ejecución de shell:

 valgrind --leak-check=yes myprog arg1 arg2 

Donde “myprog” es su progtwig comstackdo y “arg1”, “arg2” los argumentos de su progtwig.

4) El resultado es una lista de llamadas a malloc / new que no tuvieron llamadas subsecuentes para eliminar gratis.

Por ejemplo:

 ==4230== at 0x1B977DD0: malloc (vg_replace_malloc.c:136) ==4230== by 0x804990F: main (example.c:6) 

Te dice en qué línea se llamó el malloc (que no fue liberado).

Como señalado por otros, asegúrese de que por cada llamada “nueva” / “malloc” tenga una llamada subsiguiente “eliminar” / “gratis”.

En Windows, puede usar el montón de depuración CRT .

¿Hay alguna norma o procedimiento que se debe seguir para garantizar que no haya pérdida de memoria en el progtwig?

Sí, no utilices la administración manual de la memoria (si alguna vez llamas delete o delete[] manualmente, entonces lo estás haciendo mal). Use RAII y punteros inteligentes, limite las asignaciones de stack al mínimo absoluto (la mayoría de las veces, las variables automáticas serán suficientes).

Respondiendo la segunda parte de su pregunta,

¿Hay alguna norma o procedimiento que se debe seguir para garantizar que no haya pérdida de memoria en el progtwig?

Sí hay. Y esta es una de las diferencias clave entre C y C ++.

En C ++, nunca debe llamar a new o delete en su código de usuario. RAII es una técnica muy comúnmente utilizada, que prácticamente resuelve el problema de la administración de recursos. Cada recurso en su progtwig (un recurso es todo lo que tiene que ser adquirido, y luego liberado: manejadores de archivos, sockets de red, conexiones de bases de datos, pero también asignaciones de memoria simple, y en algunos casos, pares de llamadas API (BeginX ( ) / EndX (), LockY (), UnlockY ()), deben estar envueltos en una clase, donde:

  • el constructor adquiere el recurso (llamando a new si el recurso es una asignación de memroy)
  • el destructor libera el recurso,
  • la copia y la asignación se previenen (haciendo que el constructor de copias y los operadores de asignación sean privados) o se implementan para que funcionen correctamente (por ejemplo, clonando el recurso subyacente)

Esta clase se instancia luego localmente, en la stack, o como miembro de la clase, y no llamando al new y almacenando un puntero.

A menudo no necesita definir estas clases usted mismo. Los contenedores de biblioteca estándar también se comportan de esta manera, de modo que cualquier objeto almacenado en un estándar se libera cuando se destruye el vector. De nuevo, no guarde un puntero en el contenedor (lo que requeriría que llame a new y delete ), sino más bien el objeto en (que le brinda administración de memoria de forma gratuita ). Del mismo modo, las clases de punteros inteligentes se pueden usar para envolver fácilmente los objetos que tienen que ser asignados con new y controlar sus tiempos de vida.

Esto significa que cuando el objeto sale del scope, se destruye automáticamente y se libera y limpia su recurso.

Si haces esto constantemente a lo largo de tu código, simplemente no tendrás pérdidas de memoria. Todo lo que se puede filtrar está vinculado a un destructor que se garantiza que se invocará cuando el control abandone el ámbito en el que se declaró el objeto.

Visual Leak Detector (VLD) es un sistema de detección de memory leaks libre, robusto y de fuente abierta para Visual C ++.

Cuando ejecuta su progtwig en el depurador de Visual Studio, Visual Leak Detector generará un informe de pérdida de memoria al final de su sesión de depuración. El informe de fuga incluye la stack de llamadas completa que muestra cómo se asignaron los bloques de memoria filtrados. Haga doble clic en una línea en la stack de llamadas para saltar a ese archivo y línea en la ventana del editor.

Si solo tiene volcados de emergencia, puede usar el comando Windbg !heap -l , detectará los bloques de montón filtrados. Es mejor abrir la opción gflags: “Crear base de datos de seguimiento de stack de modo de usuario”, luego verá la stack de llamadas de asignación de memoria.

MTuner es una herramienta de análisis, detección de fugas y análisis de memoria multiplataforma gratuita compatible con los comstackdores MSVC, GCC y Clang. Las características incluyen:

  • línea de tiempo basada en el historial de uso de memoria y bloques de memoria en vivo
  • potente filtrado de operaciones de memoria basado en montón, etiqueta de memoria, rango de tiempo, etc.
  • SDK para instrumentación manual con código fuente completo
  • soporte continuo de integración a través del uso de línea de comando
  • árbol de stack de llamada y navegación de mapa de árbol
  • mucho más.

Los usuarios pueden perfilar cualquier plataforma de orientación de software con comstackdores cruzados GCC o Clang. MTuner viene con soporte incorporado para plataformas Windows, PlayStation 4 y PlayStation 3.

AddressSanitizer (ASan) es un detector de errores de memoria rápida. Encuentra bugs de desbordamiento de uso-after-free y {heap, stack, global} -buffer en progtwigs C / C ++. Encuentra:

  • Usar después de libre (desreferencia del puntero colgando)
  • Heap buffer overflow
  • Stack buffer overflow
  • Global buffer overflow
  • Usar después de regresar
  • Errores de orden de inicialización

Esta herramienta es muy rápida. La ralentización promedio del progtwig instrumentado es ~ 2x.

Puede usar la herramienta Valgrind para detectar memory leaks.

Además, para encontrar la fuga en una función particular, use la salida (0) al final de la función y luego ejecútela con Valgrind

 `$` valgrind ./your_CPP_program 

Además de las herramientas y métodos proporcionados en las otras torres, las herramientas de análisis de códigos estáticos se pueden usar para detectar memory leaks (y otros problemas también). Una herramienta gratuita y robusta es Cppcheck. Pero hay muchas otras herramientas disponibles. Wikipedia tiene una lista de herramientas de análisis de código estático.

Esto podría ayudar a alguien que solo quiera usar Visual Studio para la detección de fugas. Las herramientas de “Diagnostic Tools” con VS 2015 y las versiones anteriores han mejorado mucho ahora. También probó la herramienta llamada “Deleaker” pero la herramienta de Visual Studio es igualmente buena. Mirar el siguiente video me ayudó a comenzarlo.

https://www.youtube.com/watch?v=HUZW8m_3XvE

Ni “nuevo” ni “eliminar” deben usarse en el código de la aplicación. En su lugar, cree un nuevo tipo que use la expresión idiomática manager / worker, en la que la clase manager asigna y libera memoria y reenvía todas las demás operaciones al objeto worker.

Desafortunadamente, esto es más trabajo de lo que debería ser porque C ++ no tiene una sobrecarga de “operador”. Es aún más trabajo en presencia de polymorphism.

Pero vale la pena el esfuerzo porque nunca tendrás que preocuparte por las pérdidas de memoria, lo que significa que ni siquiera tienes que buscarlas.