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


180

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.

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

29
"Usualmente termino en el código con pérdida de memoria". Si usa variables automáticas, contenedores y punteros inteligentes (y sigue las mejores prácticas para usar punteros inteligentes), las pérdidas de memoria deberían ser extremadamente raras. Recuerde, en casi todos los casos, debe usar la administración automática de recursos .
James McNellis


1
@Hostile Fork: "¿cómo se puede evitar terminar generalmente en código con pérdidas de memoria" no está cubierto por esas respuestas?
Doc Brown

2
@Doc Brown: No tenía ganas de buscar eso también, pero todo está cubierto en otros lugares, como stackoverflow.com/questions/45627/…
HostileFork dice que no confíes en SE

1
Detector de fugas de bricolaje: puede poner un código sospechoso en un bucle infinito y luego abrir un administrador de tareas, por lo general, incluso una pequeña fuga llenará la memoria en segundos o minutos (eso depende de la complejidad de su código y su CPU). Si eso no sucede, ese código probablemente no tenga fugas.
Hola Mundo

Respuestas:


270

Instrucciones

Cosas que necesitarás

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

1

Comprender los conceptos básicos del operador. El operador C ++ newasigna memoria de montón. El deleteoperador libera memoria de montón. Para cada uno new, debe usar un deletepara que libere 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 la memoria solo si la has eliminado. En el siguiente código, stradquiere 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:

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

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:

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 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:

void Leak(int x){

char* p = new char [x];

// delete [] p; // Remove the first comment marking to correct.

}

5 5

Presta atención a los corchetes después de "eliminar". Úselo deletesolo para liberar un solo objeto. Úselo delete []entre 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 6

Si la filtración aún lo permite, generalmente la busco con deleaker (verifíquelo aquí: http://deleaker.com ).


3
perdón por la pregunta-comentario, pero ¿qué pasa con los parámetros de función sin punteros? someFunction("some parameter")¿Tengo que eliminar "some parameter"en someFunction, después de la llamada a la función, o se eliminan automáticamente?
19greg96

1
gracias por el enlace a Deleaker, esta es una herramienta realmente útil con una integración ordenada en Visual Studio. Podría ahorrar mucho tiempo usándolo. me señaló las líneas donde asigné memoria y no la liberé. Excelente. Y es barato, en comparación con otros buscadores de pérdida de memoria que encontré.
this.myself

@ john smith por favor explique cuál es la forma correcta de manejar casos similares al caso 3; str2 = str1; // ¡Malo! Ahora los 40 bytes son imposibles de liberar. ¿Cómo eliminar str 1 entonces?
Nihar

1
¿Qué pasa si usamos un tipo de valor como char *, int, float, ... y struct como Vector, CString, y no usamos ningún operador 'nuevo' en absoluto, no causará pérdida de memoria, ¿verdad?
123iamking

Solo estoy aquí para decir que no he tocado C ++ en casi 14 años ... pero estoy orgulloso de decir que entendí y recuerdo cómo hacer todo esto gracias a un libro de C ++ que aún poseo y leo cuando ' m aburrido con c #. Ese libro es Eficaz C ++ de Scott Mitchell. Dios, amaba ese libro. Gracias Scott!
JonH

33

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:

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

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 ,

Debug_new se refiere a una técnica en C ++ para sobrecargar y / o redefinir el operador new y el operador delete para interceptar las llamadas de asignación de memoria y desasignación, y así depurar un programa para el uso de memoria. A menudo implica definir una macro llamada DEBUG_NEW, y hace que lo nuevo se convierta en algo nuevo (_ FILE _, _ LINE _) para registrar la información del archivo / línea en la asignación.Microsoft Visual C ++ utiliza esta técnica en sus clases de Microsoft Foundation. Hay algunas formas de extender este método para evitar el uso de la redefinición de macros mientras 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 pérdidas de memoria por funciones de C como malloc. Sin embargo, puede ser muy simple de usar y también muy rápido, en comparación con algunas soluciones más completas de depuración de memoria.


44
esto #definese operator newequivocará 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.
iammilind

1
@iammilind: Por supuesto, esta técnica no es una solución integral para todos los problemas y seguramente no es aplicable en todas las situaciones.
Nawaz

@Chris_vr: auto_ptrno funcionará con contenedores estándar como std::vector, std::listetc. Vea esto: stackoverflow.com/questions/111478/…
Nawaz

Ok, genial ARCHIVO y línea se describen. ¿Qué es operator newy cuáles son estas versiones que está utilizando?

14

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:

  • si tiene que hacer su propia asignación de memoria dinámica, escriba newy deletesiempre en pares, y asegúrese de que el código de asignación / desasignación se llame en pares
  • evite la asignación dinámica de memoria si puede. Por ejemplo, use siempre vector<T> tque sea posible en lugar deT* t = new T[size]
  • use "punteros inteligentes" como impulso de 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 todas partes donde use punteros, sepa qué entidad de código es el propietario
  • aprenda qué constructores / operadores de asignación son creados automáticamente por el compilador de C ++ y qué significa eso si tiene una clase que posee un puntero (o qué significa eso si tiene una clase que contiene un puntero a un objeto que no posee).

Utilizo auto_pointer de un objeto, eso significa que eliminará todos los otros punteros de objetos de clase dentro de él.
Chris_vr

@Chris_vr: si tiene una pregunta específica sobre auto_pointer, le sugiero que haga una nueva pregunta, incluido un ejemplo.
Doc Brown

Muchas publicaciones me dicen que el vector <> no garantiza que la memoria se libere al borrar. Personalmente probé cosas de intercambio, etc. y llegué a la conclusión de que el vector <> tiene fugas, especialmente cuando se usa dinámicamente. No entiendo cómo se puede aconsejar al vector <> sobre la asignación dinámica de bricolaje usando 'nuevo' y limpieza correctamente. En mis programas integrados evito usar el vector <> para cosas dinámicas debido a todas las filtraciones. Ahí uso new o std :: list
bart s

Escribo un segundo comando debido a la cantidad de caracteres. Desafortunadamente en mi c ++ incrustado, tengo un viejo c ++ (98?) Que no tiene shrink_to_fit en un vector ... Sin embargo, el programa incrustado está 100% seguro de bloquearse completamente cuando se queda sin memoria usando el vector <> dinámicamente
bart s


8
  1. Descargar Debugging Tools para Windows .
  2. Use la gflagsutilidad para activar los seguimientos de pila en modo de usuario.
  3. Utilícelo UMDHpara 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 ejecutar UMDHy tomar las instantáneas.
  4. Ejecute UMDHnuevamente, 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.
  5. Restaura tu gflagsconfiguración anterior cuando hayas terminado.

UMDHle 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.


1
Prefiero Deleaker y Valgrind en lugar de un perfilador estándar
z0r1fan

8

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 -gbandera

3) En su ejecución de shell:

valgrind --leak-check=yes myprog arg1 arg2

Donde "miprog" es su programa compilado y arg1, arg2argumentos de su programa.

4) El resultado es una lista de llamadas a malloc/ newque no tuvieron llamadas posteriores para eliminar libremente.

Por ejemplo:

==4230==    at 0x1B977DD0: malloc (vg_replace_malloc.c:136)

==4230==    by 0x804990F: main (example.c:6)

Le dice en qué línea mallocse llamó (que no se liberó).

Como señalaron otros, asegúrese de que para cada llamada new/ malloctenga una llamada delete/ posterior free.


6

Si usa gcc, hay gprof disponible.

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

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

¿Hay algún estándar o procedimiento que se deba 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, 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).


en algún momento no podemos eliminarlo asignado en el constructor. Qué hacer en esa ocasión.
Chris_vr

5
  1. 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.

  2. Este foro discute algunas formas de evitar pérdidas de memoria en C / C ++ ..


5

Busque en su código las ocurrencias newy 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 punteros std::auto_ptr, o boost::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.


no siempre es posible eliminar la memoria asignada en el constructor. Cómo lidiar con esta situación
Chris_vr

@Chris_vr ¿Qué quieres decir? Si todos los miembros de su puntero son scope_ptrs, 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.
Mankarse

@Chris_vr: si tiene un ejemplo específico, publíquelo como una nueva pregunta, para que podamos discutirlo allí.
Doc Brown

5

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

`$` valgrind ./your_CPP_program 

5

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

#include <stdlib.h>

void * my_malloc(size_t n) {
    return malloc(n);
}

void leaky(size_t n, int do_leak) {
    void *p = my_malloc(n);
    if (!do_leak) {
        free(p);
    }
}

int main(void) {
    leaky(0x10, 0);
    leaky(0x10, 1);
    leaky(0x100, 0);
    leaky(0x100, 1);
    leaky(0x1000, 0);
    leaky(0x1000, 1);
}

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:

sudo apt-get install google-perftools
gcc -ggdb3 -o main.out main.c -ltcmalloc
PPROF_PATH=/usr/bin/google-pprof \
  HEAPCHECK=normal \
  HEAPPROFILE=ble \
  ./main.out \
;
google-pprof main.out ble.0001.heap --text

El resultado de la ejecución del programa contiene el análisis de pérdida de memoria:

WARNING: Perftools heap leak checker is active -- Performance may suffer
Starting tracking the heap
Dumping heap profile to ble.0001.heap (Exiting, 4 kB in use)
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 272 bytes in 2 objects
The 2 largest leaks:
Using local file ./main.out.
Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start


If the preceding stack traces are not enough to find the leaks, try running THIS shell command:

pprof ./main.out "/tmp/main.out.24744._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv

If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more re
Exiting with error code (instead of crashing) because of whole-program memory leaks

y la salida de google-pprofcontiene el análisis de uso del montón:

Using local file main.out.
Using local file ble.0001.heap.
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% my_malloc
     0.0   0.0% 100.0%      0.0 100.0% __libc_start_main
     0.0   0.0% 100.0%      0.0 100.0% _start
     0.0   0.0% 100.0%      0.0 100.0% leaky
     0.0   0.0% 100.0%      0.0 100.0% main

La salida nos señala dos de las tres fugas:

Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start

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:

gcc -fsanitize=address -ggdb3 -o main.out main.c
./main.out 

y salidas de ejecución:

=================================================================
==27223==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f210 in main /home/ciro/test/main.c:20
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 256 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1f2 in main /home/ciro/test/main.c:18
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1d4 in main /home/ciro/test/main.c:16
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

SUMMARY: AddressSanitizer: 4368 byte(s) leaked in 3 allocation(s).

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:

sudo apt-get install valgrind
gcc -ggdb3 -o main.out main.c
valgrind --leak-check=yes ./main.out

Salida:

==32178== Memcheck, a memory error detector
==32178== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32178== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32178== Command: ./main.out
==32178== 
==32178== 
==32178== HEAP SUMMARY:
==32178==     in use at exit: 4,368 bytes in 3 blocks
==32178==   total heap usage: 6 allocs, 3 frees, 8,736 bytes allocated
==32178== 
==32178== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091B4: main (main.c:16)
==32178== 
==32178== 256 bytes in 1 blocks are definitely lost in loss record 2 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091D2: main (main.c:18)
==32178== 
==32178== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091F0: main (main.c:20)
==32178== 
==32178== LEAK SUMMARY:
==32178==    definitely lost: 4,368 bytes in 3 blocks
==32178==    indirectly lost: 0 bytes in 0 blocks
==32178==      possibly lost: 0 bytes in 0 blocks
==32178==    still reachable: 0 bytes in 0 blocks
==32178==         suppressed: 0 bytes in 0 blocks
==32178== 
==32178== For counts of detected and suppressed errors, rerun with: -v
==32178== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

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.


4

Visual Leak Detector (VLD) es un sistema de detección de fugas de memoria de código abierto gratuito y robusto para Visual C ++.

Cuando ejecuta su programa con 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 pérdidas incluye la pila de llamadas completa que muestra cómo se asignaron los bloques de memoria perdidos. 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 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.


4

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:

  • historial basado en la línea de tiempo del uso de memoria y bloques de memoria en vivo
  • potente filtrado de operación de memoria basado en el montón, etiqueta de memoria, rango de tiempo, etc.
  • SDK para instrumentación manual con código fuente completo
  • soporte de integración continua a través del uso de la línea de comandos
  • árbol de pila de llamadas 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 integrado para plataformas Windows, PlayStation 4 y PlayStation 3.


Esta debería ser la respuesta aceptada. Es una gran herramienta y puede manejar los volúmenes de asignaciones / desasignaciones que otros no pueden.
Serge Rogatch

3

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

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

Sí, no use la administración manual de memoria (si alguna vez llama delete o delete[]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).


3

Respondiendo la segunda parte de tu pregunta,

¿Hay algún estándar o procedimiento que se deba 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 newni deleteen 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:

  • el constructor adquiere el recurso (llamandonew si el recurso es una asignación de memoria)
  • el destructor libera el recurso,
  • la copia y la asignación se evitan (al hacer que el constructor de copia y los operadores de asignación sean privados) o se implementan para que funcionen correctamente (por ejemplo, al clonar el recurso subyacente)

Esta clase se instancia localmente, en la pila o como un miembro de la clase, y no llamando newy 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::vectorse libera cuando se destruye el vector. Así que de nuevo, no almacene un puntero en el contenedor (lo que requeriría que llame newy delete), sino más bien el objeto en (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 asignarse newy 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.


si el puntero inteligente contiene una clase y esa clase contiene el puntero de varias otras clases. cuando Smart se apaga, eso significa que todo el puntero dentro se eliminará de forma segura.
Chris_vr

@Chris: suponiendo que el objeto al que apunta el puntero inteligente tiene un destructor que realiza la limpieza necesaria o el objeto contiene miembros que tienen destructores para realizar la limpieza necesaria. En esencia, siempre que cada objeto se cuide a sí mismo (limpiando después de sí mismo cuando se destruye), y mientras cada objeto se almacene por valor, no como un puntero, entonces todo lo que necesita ser liberado será liberado.
jalf

3

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:

  • Usar después de liberar (desreferencia de puntero colgante)
  • Desbordamiento del búfer de almacenamiento dinámico
  • Stack buffer overflow
  • Desbordamiento del búfer global
  • Usar después del regreso
  • Errores de orden de inicialización

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


Ver especialmente LeakSanitizer
Gabriel Devillers

0

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.


-1

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.


-3

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.