Comment trouver une fuite de mémoire dans un code / projet C ++?


180

Je suis programmeur C ++ sur la plateforme Windows. J'utilise Visual Studio 2008.

Je me retrouve généralement dans le code avec des fuites de mémoire.

Normalement, je trouve la fuite de mémoire en inspectant le code, mais c'est encombrant et n'est pas toujours une bonne approche.

Comme je ne peux pas me permettre un outil payant de détection des fuites de mémoire, je voulais que vous suggériez les meilleurs moyens d'éviter les fuites de mémoire.

  1. Je veux savoir comment le programmeur peut trouver des fuites de mémoire.
  2. Existe-t-il une norme ou une procédure à suivre pour s'assurer qu'il n'y a pas de fuite de mémoire dans le programme?

29
"Je me retrouve généralement dans le code avec une fuite de mémoire." Si vous utilisez des variables automatiques, des conteneurs et des pointeurs intelligents (et suivez les meilleures pratiques pour l'utilisation des pointeurs intelligents), les fuites de mémoire devraient être extrêmement rares. N'oubliez pas que dans presque tous les cas, vous devez utiliser la gestion automatique des ressources .
James McNellis


1
@Hostile Fork: "comment éviter de se retrouver généralement dans du code avec des fuites de mémoire" n'est pas couvert par ces réponses.
Doc Brown

2
@Doc Brown: Je n'avais pas envie de regarder ça aussi, mais tout est couvert ailleurs, comme stackoverflow.com/questions/45627
...

1
Détecteur de fuites DIY: vous pouvez mettre un code suspect dans une boucle infinie, puis ouvrir un gestionnaire de tâches, généralement même une petite fuite remplira la mémoire en quelques secondes ou minutes (cela dépend de la complexité de votre code et de votre CPU). Si cela ne se produit pas, ce morceau de code ne fuit probablement pas.
Hello World

Réponses:


270

Instructions

Les choses dont vous aurez besoin

  • Maîtrise du C ++
  • Compilateur C ++
  • Débogueur et autres outils logiciels d'enquête

1

Comprenez les bases de l'opérateur. L'opérateur C ++ newalloue la mémoire du tas. L' deleteopérateur libère la mémoire du tas. Pour tout new, vous devez utiliser un deleteafin de libérer la même mémoire que vous avez allouée:

char* str = new char [30]; // Allocate 30 bytes to house a string.

delete [] str; // Clear those 30 bytes and make str point nowhere.

2

Réallouez la mémoire uniquement si vous avez supprimé. Dans le code ci-dessous, stracquiert une nouvelle adresse avec la deuxième allocation. La première adresse est perdue irrémédiablement, de même que les 30 octets vers lesquels elle pointe. Maintenant, ils sont impossibles à libérer et vous avez une fuite de mémoire:

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

Regardez ces affectations de pointeurs. Chaque variable dynamique (mémoire allouée sur le tas) doit être associée à un pointeur. Lorsqu'une variable dynamique est dissociée de son (ses) pointeur (s), il devient impossible de l'effacer. Encore une fois, cela entraîne une fuite de mémoire:

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

Soyez prudent avec les pointeurs locaux. Un pointeur que vous déclarez dans une fonction est alloué sur la pile, mais la variable dynamique vers laquelle il pointe est allouée sur le tas. Si vous ne le supprimez pas, il persistera après la sortie du programme de la fonction:

void Leak(int x){

char* p = new char [x];

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

}

5

Faites attention aux accolades après «supprimer». À utiliser deleteseul pour libérer un seul objet. Utilisez delete []avec des crochets pour libérer un tableau de tas. Ne faites pas quelque chose comme ça:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Si la fuite est encore autorisée - je la recherche généralement avec deleaker (vérifiez-la ici: http://deleaker.com ).


3
désolé pour question-commentaire mais qu'en est-il des paramètres de fonction sans pointeurs? someFunction("some parameter")dois-je supprimer "some parameter"dans le someFunction, après l'appel de fonction, ou sont-ils automatiquement supprimés?
19greg96

1
merci pour le lien vers Deleaker, c'est un outil vraiment pratique avec une intégration soignée dans visual studio. Je pourrais gagner beaucoup de temps en l'utilisant. m'a indiqué les lignes où j'ai alloué de la mémoire et je ne l'ai pas libérée. Génial. Et il est bon marché, comparé aux autres détecteurs de fuites de mémoire que j'ai trouvés.
this.myself

@ john smith plz explique quelle est la bonne façon de gérer les cas similaires au cas 3; str2 = str1; // Mauvais! Désormais, les 40 octets sont impossibles à libérer. comment supprimer str 1 alors ??
Nihar

1
Et si nous utilisons un type valeur comme char *, int, float, ... et struct comme Vector, CString, et que nous n'utilisons pas du tout d'opérateur 'new', cela ne causera pas de fuite de mémoire, n'est-ce pas?
123iamking

Je suis juste ici pour dire que je n'ai pas touché au c ++ depuis près de 14 ans ... mais je suis fier de dire que j'ai compris et me souviens comment faire tout cela grâce à un livre c ++ que je possède et que je lis encore quand je ' m ennuyé avec c #. Ce livre est le C ++ efficace de Scott Mitchell. Dieu, j'ai adoré ce livre. Merci Scott!
JonH

33

Vous pouvez utiliser certaines techniques dans votre code pour détecter une fuite de mémoire. Le moyen le plus courant et le plus simple de détecter est de définir une macro, par exemple DEBUG_NEW, et de l'utiliser, ainsi que des macros prédéfinies telles que __FILE__et__LINE__ de localiser la fuite de mémoire dans votre code. Ces macros prédéfinies vous indiquent le fichier et le numéro de ligne des fuites de mémoire.

DEBUG_NEW est juste une MACRO qui est généralement définie comme:

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

Pour que partout où vous utilisez new , il puisse également garder une trace du fichier et du numéro de ligne qui pourraient être utilisés pour localiser une fuite de mémoire dans votre programme.

Et __FILE__, __LINE__sont des macros prédéfinies qui évaluent respectivement le nom de fichier et le numéro de ligne là où vous les utilisez!

Lisez l'article suivant qui explique très bien la technique d'utilisation de DEBUG_NEW avec d'autres macros intéressantes:

Un détecteur de fuite de mémoire multiplateforme


De Wikpedia ,

Debug_new fait référence à une technique en C ++ pour surcharger et / ou redéfinir l'opérateur new et l'opérateur delete afin d'intercepter les appels d'allocation et de désallocation de mémoire, et ainsi déboguer un programme pour l'utilisation de la mémoire. Cela implique souvent de définir une macro nommée DEBUG_NEW, et fait de nouveau quelque chose comme nouveau (_ FILE _, _ LINE _) pour enregistrer les informations de fichier / ligne sur l'allocation.Microsoft Visual C ++ utilise cette technique dans ses classes Microsoft Foundation. Il existe plusieurs façons d'étendre cette méthode pour éviter d'utiliser la redéfinition de macro tout en étant toujours capable d'afficher les informations de fichier / ligne sur certaines plates-formes. Il existe de nombreuses limitations inhérentes à cette méthode. Il s'applique uniquement à C ++ et ne peut pas détecter les fuites de mémoire par des fonctions C comme malloc. Cependant, il peut être très simple à utiliser et également très rapide, par rapport à certaines solutions de débogage de mémoire plus complètes.


4
cela #definegâchera la surcharge operator newet générera des erreurs de compilation. Même si vous réussissez à surmonter cela, les fonctions surchargées ne seront toujours pas traitées. Bien que la technique soit bonne, elle nécessite parfois de nombreux changements de code.
iammilind

1
@iammilind: Bien sûr, cette technique n'est pas une solution complète à tous les problèmes et n'est certainement pas applicable dans toutes les situations.
Nawaz

@Chris_vr: auto_ptrne fonctionnera pas avec les conteneurs standard tels que std::vector, std::listetc. Voir ceci: stackoverflow.com/questions/111478/…
Nawaz

Ok cool. Le FICHIER et la ligne sont décrits. Qu'est- operator newce que c'est et quelles sont ces versions que vous utilisez?

14

Il existe des techniques de programmation bien connues qui vous aideront à minimiser le risque de fuites de mémoire de première main:

  • si vous devez faire votre propre allocation de mémoire dynamique, écrivez new et deletetoujours par paires, et assurez-vous que le code d'allocation / désallocation est appelé par paires
  • évitez l'allocation de mémoire dynamique si vous le pouvez. Par exemple, utilisez vector<T> tautant que possible au lieu deT* t = new T[size]
  • utilisez des "pointeurs intelligents" comme les pointeurs intelligents boost ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • mon préféré: assurez-vous d'avoir bien compris le concept de propriété d'un pointeur, et assurez-vous que partout où vous utilisez des pointeurs, vous savez quelle entité de code est le propriétaire
  • Découvrez quels constructeurs / opérateurs d'affectation sont automatiquement créés par le compilateur C ++, et ce que cela signifie si vous avez une classe qui possède un pointeur (ou ce que cela signifie si vous avez une classe qui contient un pointeur vers un objet qui ne lui appartient pas ).

J'utilise auto_pointer d'un objet, cela signifie-t-il qu'il supprimera tous les autres pointeurs d'objet de classe à l'intérieur.
Chris_vr

@Chris_vr: si vous avez une question spécifique sur auto_pointer, je vous suggère de faire une nouvelle question, avec un exemple.
Doc Brown

De nombreux articles me disent que vector <> ne garantit pas la libération de la mémoire en cas d'effacement. J'ai personnellement testé des trucs de swap, etc. et j'en suis venu à la conclusion que vector <> fuit surtout lorsqu'il est utilisé dynamiquement. Je ne comprends pas comment vector <> peut être conseillé par rapport à l'allocation dynamique à faire soi-même en utilisant correctement «nouveau» et le nettoyage. Dans mes programmes embarqués, j'évite d'utiliser vector <> pour des trucs dynamiques à cause de toutes les fuites. Là, j'utilise new ou std :: list
bart s

Je tape une deuxième commande à cause du nombre de caractères. Malheureusement, dans mon c ++ intégré, j'ai un ancien c ++ (98?) Qui n'a pas shrink_to_fit sur un vecteur ... Cependant, le programme intégré est sûr à 100% de planter totalement lorsqu'il manque de mémoire en utilisant le vecteur <> dynamiquement
bart s


8
  1. Télécharger les outils de débogage pour Windows .
  2. Utilisez le gflags utilitaire pour activer les traces de pile en mode utilisateur.
  3. Utilisez UMDHpour prendre plusieurs instantanés de la mémoire de votre programme. Prenez un instantané avant que la mémoire ne soit allouée et prenez un deuxième instantané après un moment où vous pensez que votre programme a perdu de la mémoire. Vous voudrez peut-être ajouter des pauses ou des invites dans votre programme pour vous donner une chance d'exécuter UMDHet de prendre les instantanés.
  4. Exécutez à UMDHnouveau, cette fois dans son mode qui fait une différence entre les deux instantanés. Il générera ensuite un rapport contenant les piles d'appels des fuites de mémoire suspectées.
  5. Restaurez vos gflagsparamètres précédents lorsque vous avez terminé.

UMDHvous donnera plus d'informations que le tas de débogage CRT car il surveille les allocations de mémoire sur l'ensemble de votre processus; il peut même vous dire si des composants tiers fuient.


1
Je préfère Deleaker et Valgrind au lieu du profileur standard
z0r1fan

8

L'exécution de «Valgrind» peut:

1) Aide à identifier les fuites de mémoire - indique le nombre de fuites de mémoire dont vous disposez et indiquez les lignes du code où la mémoire perdue a été allouée.

2) Soulignez les mauvaises tentatives de libérer de la mémoire (par exemple, un mauvais appel de delete)

Instructions d'utilisation de "Valgrind"

1) Obtenez valgrind ici .

2) Compilez votre code avec -gdrapeau

3) Dans votre exécution de shell:

valgrind --leak-check=yes myprog arg1 arg2

Où "myprog" est votre programme compilé et arg1,arg2 les arguments de votre programme.

4) Le résultat est une liste d'appels vers malloc/ newqui n'ont pas eu d'appels ultérieurs à supprimer gratuitement.

Par exemple:

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

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

Vous indique dans quelle ligne le malloc(qui n'a pas été libéré) a été appelé.

Comme indiqué par d'autres, assurez-vous que pour chaque appel new/ malloc, vous avez un appel delete/ suivant free.


6

Si vous utilisez gcc, gprof est disponible.

Je voulais savoir comment le programmeur trouve une fuite de mémoire

Certains utilisent des outils, certains font ce que vous faites, peuvent également être examinés par des pairs

Existe-t-il une norme ou une procédure à suivre pour s'assurer qu'il n'y a pas de fuite de mémoire dans le programme

Pour moi: chaque fois que je crée des objets alloués dynamiquement, je mets toujours le code de libération après, puis remplis le code entre. Ce serait OK si vous êtes sûr qu'il n'y aura pas d'exceptions dans le code entre les deux. Sinon, j'utilise try-finally (je n'utilise pas fréquemment C ++).


un certain temps, nous ne pouvons pas supprimer alloué dans le constructeur.ce qu'il faut faire à cette occasion.
Chris_vr

5
  1. Dans Visual Studio, il existe un détecteur intégré de fuite de mémoire appelé C Runtime Library. Lorsque votre programme se termine après le retour de la fonction principale, CRT vérifie le tas de débogage de votre application. si vous avez encore des blocs alloués sur le tas de débogage, vous avez une fuite de mémoire.

  2. Ce forum aborde quelques moyens d'éviter les fuites de mémoire en C / C ++.


5

Recherchez dans votre code les occurrences de newet assurez-vous qu'elles se produisent toutes dans un constructeur avec une suppression correspondante dans un destructeur. Assurez-vous qu'il s'agit de la seule opération de lancement possible dans ce constructeur. Un moyen simple de le faire est d'envelopper tous les pointeurs std::auto_ptr, ou boost::scoped_ptr(selon que vous avez besoin ou non de la sémantique de déplacement). Pour tout code futur, assurez-vous simplement que chaque ressource appartient à un objet qui nettoie la ressource dans son destructeur. Si vous avez besoin d'une sémantique de déplacement, vous pouvez passer à un compilateur prenant en charge les références de valeur r (VS2010, je crois) et créer des constructeurs de déplacement. Si vous ne voulez pas faire cela, vous pouvez utiliser une variété de techniques délicates impliquant une utilisation consciencieuse du swap, ou essayer la bibliothèque Boost.Move.


il n'est pas toujours possible de supprimer la mémoire allouée dans le constructeur
Comment

@Chris_vr Que voulez-vous dire? Si tous vos membres pointeurs sont des scope_ptrs et que chacun est initialisé individuellement, tous ceux qui ont été construits avec succès supprimeront leurs pointeurs, et les autres ne contiendront pas encore de pointeurs vers la mémoire allouée de toute façon. Je donnerai un exemple dans quelques heures quand je rentrerai du travail.
Mankarse

@Chris_vr: si vous avez un exemple spécifique, postez-le comme une nouvelle question, afin que nous puissions en discuter ici.
Doc Brown

5

Vous pouvez utiliser l'outil Valgrind pour détecter les fuites de mémoire.

Aussi, pour trouver la fuite dans une fonction particulière, utilisez exit (0) à la fin de la fonction puis exécutez-la avec Valgrind

`$` valgrind ./your_CPP_program 

5

Une étude des vérificateurs automatiques de fuites de mémoire

Dans cette réponse, je compare plusieurs vérificateurs de fuite de mémoire différents dans un exemple de fuite de mémoire simple et facile à comprendre.

Avant toute chose, voyez cet énorme tableau dans le wiki ASan qui compare tous les outils connus de l'homme: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

L'exemple analysé sera:

principal c

#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 en amont .

Nous allons essayer de voir dans quelle mesure les différents outils nous indiquent clairement les appels qui fuient.

tcmalloc de gperftools par Google

https://github.com/gperftools/gperftools

Utilisation sur 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

La sortie de l'exécution du programme contient l'analyse des fuites de mémoire:

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

et la sortie de google-pprofcontient l'analyse de l'utilisation du tas:

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 sortie nous indique deux des trois fuites:

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

Je ne sais pas pourquoi le troisième ne s'est pas présenté

Dans tous les cas, quand généralement quand quelque chose fuit, cela arrive souvent, et quand je l'ai utilisé sur un vrai projet, j'ai fini par me faire remarquer très facilement la fonction de fuite.

Comme mentionné sur la sortie elle-même, cela entraîne un ralentissement d'exécution significatif.

Documentation supplémentaire sur:

Voir aussi: Comment utiliser TCMalloc?

Testé dans Ubuntu 19.04, google-perftools 2.5-2.

Address Sanitizer (ASan) également par Google

https://github.com/google/sanitizers

Précédemment mentionné sur: Comment trouver une fuite de mémoire dans un code / projet C ++? TODO contre tcmalloc.

Ceci est déjà intégré dans GCC, vous pouvez donc simplement faire:

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

et sorties d'exécution:

=================================================================
==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).

qui identifie clairement toutes les fuites. Agréable!

ASan peut également effectuer d'autres vérifications intéressantes telles que des écritures hors limites: Smashing de pile détecté

Testé dans Ubuntu 19.04, GCC 8.3.0.

Valgrind

http://www.valgrind.org/

Précédemment mentionné sur: https://stackoverflow.com/a/37661630/895245

Usage:

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

Production:

==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)

Donc, encore une fois, toutes les fuites ont été détectées.

Voir aussi: Comment utiliser valgrind pour trouver des fuites de mémoire?

Testé dans Ubuntu 19.04, valgrind 3.14.0.


4

Visual Leak Detector (VLD) est un système de détection de fuite de mémoire libre, robuste et open source pour Visual C ++.

Lorsque vous exécutez votre programme sous le débogueur Visual Studio, Visual Leak Detector génère un rapport de fuite de mémoire à la fin de votre session de débogage. Le rapport de fuite inclut la pile d'appels complète montrant comment les blocs de mémoire perdus ont été alloués. Double-cliquez sur une ligne dans la pile d'appels pour accéder à ce fichier et à cette ligne dans la fenêtre de l'éditeur.

Si vous n'avez que des vidages sur incident, vous pouvez utiliser la !heap -lcommande Windbg , elle détectera les blocs de tas perdus. Mieux vaut ouvrir l'option gflags: «Créer une base de données de trace de pile en mode utilisateur», vous verrez alors la pile des appels d'allocation de mémoire.


4

MTuner est un outil gratuit de profilage de mémoire multi-plateforme, de détection des fuites et d'analyse prenant en charge les compilateurs MSVC, GCC et Clang. Les fonctionnalités incluent:

  • historique basé sur la chronologie de l'utilisation de la mémoire et des blocs de mémoire en direct
  • filtrage puissant des opérations de mémoire basé sur le tas, l'étiquette de mémoire, la plage de temps, etc.
  • SDK pour instrumentation manuelle avec code source complet
  • prise en charge de l'intégration continue grâce à l'utilisation de la ligne de commande
  • Arborescence des appels et navigation sur la carte de l'arborescence
  • beaucoup plus.

Les utilisateurs peuvent profiler toutes les plates-formes de ciblage de logiciels avec des compilateurs croisés GCC ou Clang. MTuner est livré avec une prise en charge intégrée des plates-formes Windows, PlayStation 4 et PlayStation 3.


Cela devrait être la réponse acceptée. C'est un excellent outil et peut gérer les volumes d'allocations / désallocations que d'autres ne peuvent pas.
Serge Rogatch

3

Sous Windows, vous pouvez utiliser le tas de débogage CRT .

Existe-t-il une norme ou une procédure à suivre pour s'assurer qu'il n'y a pas de fuite de mémoire dans le programme?

Ouais, n'utilisez pas la gestion manuelle de la mémoire (si jamais vous appelez deleteou delete[]manuellement, vous le faites mal). Utilisez RAII et des pointeurs intelligents, limitez les allocations de tas au minimum absolu (la plupart du temps, des variables automatiques suffiront).


3

Répondant à la deuxième partie de votre question,

Existe-t-il une norme ou une procédure à suivre pour s'assurer qu'il n'y a pas de fuite de mémoire dans le programme?

Oui il y a. Et c'est l'une des principales différences entre C et C ++.

En C ++, vous ne devez jamais appeler newni deletedans votre code utilisateur. RAII est une technique très couramment utilisée, qui résout à peu près le problème de gestion des ressources. Chaque ressource de votre programme (une ressource est tout ce qui doit être acquis, puis libéré plus tard: descripteurs de fichiers, sockets réseau, connexions de base de données, mais aussi allocations de mémoire simples et, dans certains cas, paires d'appels d'API (BeginX ( ) / EndX (), LockY (), UnlockY ()), doivent être enveloppés dans une classe, où:

  • le constructeur acquiert la ressource (en appelantnew si la ressource est une allocation memroy)
  • le destructeur libère la ressource,
  • la copie et l'affectation sont soit empêchées (en rendant le constructeur de copie et les opérateurs d'affectation privés), soit implémentées pour fonctionner correctement (par exemple en clonant la ressource sous-jacente)

Cette classe est ensuite instanciée localement, sur la pile ou en tant que membre de classe, et non en appelant newet en stockant un pointeur.

Vous n'avez souvent pas besoin de définir ces classes vous-même. Les conteneurs de bibliothèque standard se comportent également de cette manière, de sorte que tout objet stocké dans un std::vectorest libéré lorsque le vecteur est détruit. Encore une fois, ne stockez pas de pointeur dans le conteneur (ce qui vous obligerait à appeler newet delete), mais plutôt l'objet lui-même (qui vous permet de gérer la mémoire gratuitement ). De même, les classes de pointeurs intelligents peuvent être utilisées pour encapsuler facilement des objets qui doivent simplement être alloués newet contrôler leur durée de vie.

Cela signifie que lorsque l'objet sort de la portée, il est automatiquement détruit et sa ressource libérée et nettoyée.

Si vous faites cela de manière cohérente tout au long de votre code, vous n'aurez tout simplement aucune fuite de mémoire. Tout ce qui pourrait être divulgué est lié à un destructeur dont l'appel est garanti lorsque le contrôle quitte la portée dans laquelle l'objet a été déclaré.


si le pointeur intelligent contient une classe et que cette classe contient le pointeur de plusieurs autres classes. lorsque smart s'éteint, cela signifie que tout le pointeur à l'intérieur sera supprimé en toute sécurité.
Chris_vr

@Chris: en supposant que l'objet pointé par le pointeur intelligent a un destructeur qui effectue le nettoyage nécessaire ou que l'objet contient des membres qui ont eux-mêmes des destructeurs pour effectuer le nettoyage nécessaire. En substance, tant que chaque objet prend soin de lui-même (se nettoyant après lui-même lorsqu'il est détruit), et tant que chaque objet est stocké par valeur, pas comme pointeur, alors tout ce qui doit être libéré sera libéré.
jalf

3

AddressSanitizer (Asan) est un détecteur d'erreur de mémoire rapide. Il trouve des bogues de dépassement de capacité d'utilisation après libre et {tas, pile, global} -buffer dans les programmes C / C ++. Il trouve:

  • Utiliser après libre (déréférencement du pointeur pendant)
  • Dépassement de la mémoire tampon du tas
  • Dépassement de la mémoire tampon de la pile
  • Dépassement de tampon global
  • Utiliser après retour
  • Bogues d'ordre d'initialisation

Cet outil est très rapide. Le ralentissement moyen du programme instrumenté est de ~ 2x.



0

En plus des outils et méthodes fournis dans les autres réponses, des outils d'analyse de code statique peuvent être utilisés pour détecter les fuites de mémoire (et d'autres problèmes également). Un outil gratuit et robuste est Cppcheck. Mais il existe de nombreux autres outils disponibles. Wikipédia propose une liste d'outils d'analyse de code statique.


-1

Assurez-vous que toute la mémoire du tas est libérée avec succès. Il n'est pas nécessaire si vous n'allouez jamais de mémoire sur le tas. Si vous le faites, comptez le nombre de fois que vous avez de la mémoire malloc, et comptez le nombre de fois que vous libérez de la mémoire.


-3

Ni «nouveau» ni «supprimer» ne doivent jamais être utilisés dans le code d'application. À la place, créez un nouveau type qui utilise l'idiome manager / worker, dans lequel la classe manager alloue et libère de la mémoire et transmet toutes les autres opérations à l'objet worker.

Malheureusement, c'est plus de travail qu'il ne devrait l'être car C ++ n'a pas de surcharge de "opérateur". C'est encore plus de travail en présence de polymorphisme.

Mais cela en vaut la peine, car vous n'avez plus jamais à vous soucier des fuites de mémoire, ce qui signifie que vous n'avez même pas à les rechercher.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.