leak - ¿Cómo encontrar la pérdida de memoria en un código/proyecto de C++?
memory leak requisitos (17)
- Descargue Herramientas de depuración para Windows .
- Use la utilidad
gflags
para activar losgflags
pila de modo de usuario. - Use
UMDH
para tomar varias instantáneas de la memoria de su programa. 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 programa ha filtrado la memoria. Es posible que desee agregar pausas o indicaciones en su programa para darle la oportunidad de ejecutarUMDH
y tomar las instantáneas. - 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 pilas de llamadas de pérdidas de memoria sospechosas. - 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.
Soy un programador 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 fugas de memoria pagada, quería que sugirieran la mejor manera de evitar fugas de memoria.
- Quiero saber cómo el programador puede encontrar pérdidas de memoria.
- ¿Hay algún estándar o procedimiento que se debe seguir para garantizar que no haya pérdida de memoria en el programa?
En Visual Studio, hay un detector incorporado para la pérdida de memoria llamado C Runtime Library. Cuando su programa 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.
Este foro analiza algunas formas de evitar la pérdida de memoria en C / C ++.
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 programas 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 programa instrumentado es ~ 2x.
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 fugas de memoria (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.
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 compilador 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.
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 programa?
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 pila al mínimo absoluto (la mayoría de las veces, las variables automáticas serán suficientes).
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.
Existen algunas técnicas de programación bien conocidas que lo ayudarán a minimizar el riesgo de tener fugas de memoria de primera mano:
- si tiene que hacer su propia asignación de memoria dinámica, escriba
new
ydelete
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> t
donde sea posible en lugar deT* 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 compilador 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).
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 polimorfismo.
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.
Puede usar algunas técnicas en su código para detectar fugas de memoria. 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 programa.
¡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 programa 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 fugas de memoria 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.
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 programa?
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 programa (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 pila, 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 sí (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 alcance, 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.
Si usas gcc, hay gprof disponible.
Quería saber cómo el programador 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 programa?
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).
MTuner es una herramienta de análisis, detección de fugas y análisis de memoria multiplataforma gratuita compatible con los compiladores 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 pila de llamada y navegación de mapa de árbol
- mucho más.
Los usuarios pueden perfilar cualquier plataforma de orientación de software con compiladores cruzados GCC o Clang. MTuner viene con soporte incorporado para plataformas Windows, PlayStation 4 y PlayStation 3.
Visual Leak Detector (VLD) es un sistema de detección de fugas de memoria libre, robusto y de fuente abierta para Visual C ++.
Cuando ejecuta su programa 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 pila de llamadas completa que muestra cómo se asignaron los bloques de memoria filtrados. Haga doble clic en una línea en la pila 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 pila de modo de usuario", luego verá la pila de llamadas de asignación de memoria.
Ejecutar "Valgrind" puede:
1) Ayuda a identificar fugas de memoria : le muestra cuántas fugas de memoria tiene y señala las líneas en el código donde se asignó la memoria filtrada.
2) Señale los intentos incorrectos de liberar memoria (por ejemplo, llamada impropia de "eliminar")
Instrucciones para usar "Valgrind"
1) Obtenga valgrind here .
1) Compila 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 programa compilado y "arg1", "arg2" los argumentos de su programa.
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".
Instrucciones
Cosas que necesitarás
- Competencia en C ++
- Compilador 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 pila. 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 pila, 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 programa 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!