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 pérdida de memoria al inspeccionar el código, pero es engorroso y no siempre es un buen enfoque.
Como no puedo permitirme una herramienta de detección de pérdida de memoria paga, quería que sugirieran las mejores formas posibles de evitar pérdidas de memoria.
- Quiero saber cómo el programador puede encontrar pérdidas de memoria.
- ¿Hay algún estándar o procedimiento que se deba seguir para garantizar que no haya pérdida de memoria en el programa?
c++
memory-leaks
Chris_vr
fuente
fuente
Respuestas:
Instrucciones
Cosas que necesitarás
1
Comprender los conceptos básicos del operador. El operador C ++
new
asigna memoria de montón. Eldelete
operador libera memoria de montón. Para cada unonew
, debe usar undelete
para que libere la misma memoria que asignó:2
Reasigna la memoria solo si la 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, al igual que los 30 bytes a los que apunta. Ahora son imposibles de liberar y tienes una pérdida de memoria:3
Mire esas asignaciones de puntero. Cada variable dinámica (memoria asignada en el montón) debe asociarse con un puntero. Cuando una variable dinámica se disocia de su puntero (s), se hace imposible borrarla. Nuevamente, esto resulta en una pérdida de memoria:
4 4
Tenga cuidado con los punteros 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:
5 5
Presta atención a los corchetes después de "eliminar". Úselo
delete
solo para liberar un solo objeto. Úselodelete []
entre corchetes para liberar una matriz de montón. No hagas algo como esto:6 6
Si la filtración aún lo permite, generalmente la busco con deleaker (verifíquelo aquí: http://deleaker.com ).
fuente
someFunction("some parameter")
¿Tengo que eliminar"some parameter"
ensomeFunction
, después de la llamada a la función, o se eliminan automáticamente?Puede usar algunas técnicas en su código para detectar pérdidas de memoria. La forma más común y más fácil de detectar es, definir una macro decir, DEBUG_NEW y usarla, junto con macros predefinidas 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 una MACRO que generalmente se define como:
De modo que donde sea que use
new
, también puede realizar un seguimiento del archivo y el número de línea que podrían usarse para localizar la pérdida de memoria en su programa.Y
__FILE__
,__LINE__
son macros predefinidas que evalúan al número y nombre de archivo de la 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 bien:
Un detector de fugas de memoria multiplataforma
De Wikpedia ,
fuente
#define
seoperator new
equivocará con sobrecarga y generará errores de compilación. Incluso si logra superar eso, las funciones sobrecargadas no se abordarán. Aunque la técnica es buena, a veces necesita muchos cambios de código.auto_ptr
no funcionará con contenedores estándar comostd::vector
,std::list
etc. Vea esto: stackoverflow.com/questions/111478/…operator new
y cuáles son estas versiones que está utilizando?Existen algunas técnicas de programación bien conocidas que lo ayudarán a minimizar el riesgo de pérdida de memoria de primera mano:
new
ydelete
siempre en pares, y asegúrese de que el código de asignación / desasignación se llame en paresvector<T> t
que sea posible en lugar deT* t = new T[size]
fuente
Valgrind http://valgrind.org/
y
GDB http://www.gnu.org/software/gdb/
fuente
gflags
utilidad para activar los seguimientos de pila en modo de usuario.UMDH
para tomar múltiples instantáneas de la memoria de su programa. Tome una instantánea antes de que se asigne la memoria, y tome una segunda instantánea después de un punto en el que cree que su programa ha perdido memoria. Es posible que desee agregar pausas o indicaciones en su programa para darle la oportunidad de ejecutarUMDH
y tomar las instantáneas.UMDH
nuevamente, esta vez en su modo que hace una diferencia entre las dos instantáneas. Luego generará un informe que contiene las pilas de llamadas de pérdidas de memoria sospechosas.gflags
configuración anterior cuando hayas terminado.UMDH
le dará más información que el montón de depuración de CRT porque está mirando las asignaciones de memoria en todo su proceso; incluso puede decirle si hay fugas en componentes de terceros.fuente
Ejecutar "Valgrind" puede:
1) Ayuda a identificar pérdidas de memoria: le muestra cuántas pérdidas de memoria tiene y señale las líneas en el código donde se asignó la memoria perdida.
2) Señale intentos incorrectos de liberar memoria (por ejemplo, llamada incorrecta de
delete
)Instrucciones para usar "Valgrind"
1) Obtén valgrind aquí .
2) Compila tu código con
-g
bandera3) En su ejecución de shell:
Donde "miprog" es su programa compilado y
arg1
,arg2
argumentos de su programa.4) El resultado es una lista de llamadas a
malloc
/new
que no tuvieron llamadas posteriores para eliminar libremente.Por ejemplo:
Le dice en qué línea
malloc
se llamó (que no se liberó).Como señalaron otros, asegúrese de que para cada llamada
new
/malloc
tenga una llamadadelete
/ posteriorfree
.fuente
Si usa gcc, hay gprof disponible.
Algunos usan herramientas, otros hacen lo que tú haces, también podrían hacerlo a través de la revisión de código de pares
Para mí: cada vez que creo objetos asignados dinámicamente, siempre pongo el código de liberación después, y luego relleno el código entre ellos. Esto estaría bien si está seguro de que no habrá excepciones en el código intermedio. De lo contrario, uso try-finally (no uso C ++ con frecuencia).
fuente
En Visual Studio, hay un detector incorporado para pérdida de memoria llamado C Runtime Library. Cuando su programa se cierra después de que vuelve la función principal, CRT verificará el montón de depuración de su aplicación. Si todavía tiene bloques asignados en el montón de depuración, entonces tiene pérdida de memoria.
Este foro discute algunas formas de evitar pérdidas de memoria en C / C ++ ..
fuente
Busque en su código las ocurrencias
new
y asegúrese de que todas ocurran dentro de un constructor con una eliminación coincidente en un destructor. Asegúrese de que esta sea la única operación de lanzamiento posible en ese constructor. Una manera simple de hacer esto es envolver todos los punterosstd::auto_ptr
, oboost::scoped_ptr
(dependiendo de si necesita o no mover semántica). Para todo el código futuro, 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 desea hacerlo, puede utilizar una variedad de técnicas complicadas que implican el uso concienzudo de swap, o probar la biblioteca Boost.Move.fuente
scope_ptr
s, y cada uno se inicializa individualmente, todos los que se construyeron con éxito eliminarán sus punteros, y los demás aún no mantendrán los punteros en la memoria asignada de todos modos. Pondré un ejemplo dentro de unas horas cuando llegue a casa del trabajo.Puede usar la herramienta Valgrind para detectar pérdidas de memoria.
Además, para encontrar la fuga en una función en particular, use exit (0) al final de la función y luego ejecútela con Valgrind
fuente
Una encuesta de verificadores automáticos de pérdidas de memoria
En esta respuesta, comparo varios verificadores de pérdida de memoria diferentes en un ejemplo simple y fácil de entender de pérdida de memoria.
Antes de nada, vea esta gran tabla en el wiki de ASan que compara todas las herramientas conocidas por el hombre: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924
El ejemplo analizado será:
C Principal
GitHub aguas arriba .
Intentaremos ver con qué claridad las diferentes herramientas nos señalan las llamadas con fugas.
tcmalloc de gperftools por Google
https://github.com/gperftools/gperftools
Uso en Ubuntu 19.04:
El resultado de la ejecución del programa contiene el análisis de pérdida de memoria:
y la salida de
google-pprof
contiene el análisis de uso del montón:La salida nos señala dos de las tres fugas:
No estoy seguro de por qué no apareció el tercero
En cualquier caso, cuando generalmente cuando algo se filtra, sucede muchas veces, y cuando lo usé en un proyecto real, terminé siendo señalado a la función de fuga muy fácilmente.
Como se menciona en la salida en sí, esto incurre en una desaceleración de ejecución significativa.
Documentación adicional en:
Ver también: ¿Cómo usar TCMalloc?
Probado en Ubuntu 19.04, google-perftools 2.5-2.
Address Sanitizer (ASan) también de Google
https://github.com/google/sanitizers
Mencionado anteriormente en: ¿Cómo encontrar la pérdida de memoria en un código / proyecto C ++? TODO vs tcmalloc.
Esto ya está integrado en GCC, por lo que puede hacer lo siguiente:
y salidas de ejecución:
que identifica claramente todas las fugas. ¡Agradable!
ASan también puede hacer otras comprobaciones interesantes, como escrituras fuera de los límites: se detectó el aplastamiento de la pila
Probado en Ubuntu 19.04, GCC 8.3.0.
Valgrind
http://www.valgrind.org/
Mencionado anteriormente en: https://stackoverflow.com/a/37661630/895245
Uso:
Salida:
Entonces, una vez más, se detectaron todas las fugas.
Ver también: ¿Cómo uso valgrind para encontrar pérdidas de memoria?
Probado en Ubuntu 19.04, valgrind 3.14.0.
fuente
Visual Leak Detector (VLD) es un sistema de detección de fugas de memoria de código abierto gratuito y robusto para Visual C ++.
Si solo tiene volcados de memoria
!heap -l
, puede usar el comando Windbg , detectará los bloques de montón perdidos . Mejor abra la opción gflags: "Crear base de datos de seguimiento de pila en modo usuario", luego verá la pila de llamadas de asignación de memoria.fuente
MTuner es una herramienta gratuita de análisis de detección de fugas, detección de fugas y análisis multiplataforma que admite compiladores MSVC, GCC y Clang. Las características incluyen:
Los usuarios pueden perfilar cualquier plataforma de orientación de software con compiladores cruzados GCC o Clang. MTuner viene con soporte integrado para plataformas Windows, PlayStation 4 y PlayStation 3.
fuente
En Windows puede usar el montón de depuración CRT .
Sí, no use la administración manual de memoria (si alguna vez llama
delete
odelete[]
manualmente, entonces lo está haciendo mal). Utilice RAII y punteros inteligentes, limite las asignaciones de almacenamiento dinámico al mínimo absoluto (la mayoría de las veces, las variables automáticas serán suficientes).fuente
Respondiendo la segunda parte de tu pregunta,
Sí hay. Y esta es una de las diferencias clave entre C y C ++.
En C ++, nunca debe llamar
new
nidelete
en su código de usuario. RAII es una técnica muy utilizada, que prácticamente resuelve el problema de gestión de recursos. Cada recurso en su programa (un recurso es cualquier cosa que deba adquirirse y luego liberarse: identificadores 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 incluirse en una clase, donde:new
si el recurso es una asignación de memoria)Esta clase se instancia localmente, en la pila o como un miembro de la clase, y no llamando
new
y almacenando un puntero.A menudo no necesita definir estas clases usted mismo. Los contenedores de la biblioteca estándar también se comportan de esta manera, de modo que cualquier objeto almacenado en un
std::vector
se libera cuando se destruye el vector. Así que de nuevo, no almacene un puntero en el contenedor (lo que requeriría que llamenew
ydelete
), sino más bien el objeto en sí (que le brinda administración de memoria de forma gratuita ). Del mismo modo, las clases de puntero inteligente se pueden usar para envolver fácilmente objetos que solo tienen que asignarsenew
y controlar sus vidas.Esto significa que cuando el objeto sale del alcance, se destruye automáticamente y se liberan y limpian sus recursos.
Si hace esto constantemente a lo largo de su código, simplemente no tendrá pérdidas de memoria. Todo lo que podría filtrarse está vinculado a un destructor que se garantiza que se llamará cuando el control abandone el ámbito en el que se declaró el objeto.
fuente
AddressSanitizer (ASAN) es un detector de error de memoria rápida. Encuentra errores de desbordamiento de búfer de uso libre y {heap, stack, global} en programas C / C ++. Encuentra:
Esta herramienta es muy rápida. La desaceleración promedio del programa instrumentado es ~ 2x.
fuente
Además de las herramientas y métodos proporcionados en las otras respuestas, las herramientas de análisis de código estático se pueden utilizar para detectar pérdidas de memoria (y también otros problemas). 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.
fuente
Asegúrese de que toda la memoria de almacenamiento dinámico se haya liberado correctamente. No es necesario si nunca asigna memoria en el montón. Si lo hace, cuente la cantidad de veces que malloca la memoria y cuente la cantidad de tiempo que libera memoria.
fuente
Ni "nuevo" ni "eliminar" deben usarse en el código de la aplicación. En su lugar, cree un nuevo tipo que utilice el lenguaje de administrador / trabajador, en el que la clase de administrador asigna y libera memoria y reenvía todas las demás operaciones al objeto trabajador.
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, ya que nunca tendrá que preocuparse por las pérdidas de memoria, lo que significa que ni siquiera tiene que buscarlas.
fuente