Comment détecter / éviter les fuites de mémoire dans votre code (non géré)? [fermé]


125

Dans le code C / C ++ non managé, quelles sont les meilleures pratiques pour détecter les fuites de mémoire? Et les directives de codage à éviter? (Comme si c'était aussi simple que ça;)

Nous avons utilisé une méthode un peu stupide dans le passé: avoir un compteur incrémenté pour chaque appel d'allocation de mémoire et décrémenter lors de la libération. À la fin du programme, la valeur du compteur doit être zéro.

Je sais que ce n'est pas un excellent moyen et qu'il y a quelques prises. (Par exemple, si vous libérez de la mémoire qui a été allouée par un appel d'API de plate-forme, votre nombre d'allocations ne correspondra pas exactement à votre nombre de libérations. Bien sûr, nous avons incrémenté le compteur lors de l'appel des appels d'API qui ont alloué de la mémoire.)

J'attends vos expériences, suggestions et peut-être quelques références à des outils qui simplifient cela.


En termes d'éviter les fuites, le post suivant a quelques conseils: http://stackoverflow.com/questions/27492/c-memory-management
tonylo


J'ai utilisé celui-ci avec Visual Studio pour détecter les fuites de mémoire. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo

1
vous recherchez valgrin (pour linux) ou deleaker (pour Windows), regardez également le détecteur de fuite visuel ...
John Smith

pour trouver des fuites de mémoire, vérifiez ici: theunixshell.blogspot.com/2013/11/…
Vijay

Réponses:


78

Si votre code C / C ++ est portable vers * nix, peu de choses sont meilleures que Valgrind .


1
Valgrind fonctionne également maintenant sous OS X, donc Linux n'est pas votre seule option.
Michael Anderson

1
Valgrind pour Linux (et OS X). Si vous utilisez windose - deleaker - le meilleur de tous!
John Smith

@JordiBunster: Bien! Mais basé sur le runtime. Avec une grande base de code (écrite en C dans le cas ly), vous testerez principalement votre programme pour la façon dont il a été conçu. Un attaquant peut mettre les quelques milliers d'heures nécessaires à la lecture du code pour trouver un exploit de fuite mémoire. J'aurais attendu un outil automatisé pour l'analyse du code source similaire à ce qui existe pour JavaScript.
user2284570

65

Si vous utilisez Visual Studio, Microsoft fournit des fonctions utiles pour détecter et déboguer les fuites de mémoire.

Je commencerais par cet article: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

Voici le résumé rapide de ces articles. Tout d'abord, incluez ces en-têtes:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

Ensuite, vous devez appeler ceci lorsque votre programme se termine:

_CrtDumpMemoryLeaks();

Alternativement, si votre programme ne se termine pas au même endroit à chaque fois, vous pouvez l'appeler au début de votre programme:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Désormais, lorsque le programme sortira, toutes les allocations qui n'étaient pas libres seront imprimées dans la fenêtre de sortie avec le fichier dans lequel elles ont été allouées et l'occurrence d'allocation.

Cette stratégie fonctionne pour la plupart des programmes. Cependant, cela devient difficile voire impossible dans certains cas. L'utilisation de bibliothèques tierces qui effectuent une initialisation au démarrage peut entraîner l'apparition d'autres objets dans le vidage de la mémoire et rendre difficile le suivi de vos fuites. En outre, si l'une de vos classes a des membres avec le même nom que l'une des routines d'allocation de mémoire (comme malloc), les macros de débogage CRT poseront des problèmes.

Il existe d'autres techniques expliquées dans le lien MSDN référencé ci-dessus qui pourraient également être utilisées.


Une note à propos de cette méthode: il semble que cela ne fonctionne que si vous utilisez du C pur avec malloc et gratuit. Le rapport détaillé qui comprend les numéros de ligne n'est pas créé si vous utilisez new et delete de c ++.
Zach le

2
@Zach: en fait, vous pouvez aussi faire fonctionner cela (pour tout code que vous compilez vous-même, de toute façon) - voir la réponse acceptée dans social.msdn.microsoft.com/forums/en-US/vcgeneral/thread
Roman Starkov

Cela fonctionnera-t-il également en mode version?
JV

1
@ user3152463 Non. D'après la documentation, cela ne fonctionnera que pour la version de débogage: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Dusty Campbell

Cette ligne est fausse: #define CRTDBG_MAP_ALLOC Cela devrait être: #define _CRTDBG_MAP_ALLOC
Fallso

37

En C ++: utilisez RAII. Pointeurs intelligents comme std::unique_ptr, std::shared_ptr, std::weak_ptrsont vos amis.


1
et std: vector est un excellent remplacement lorsque les tableaux (tampons) sont désalloués dans la même fonction qu'ils sont alloués.
KJAWolf

4
Au moins std :: auto_ptr et boost :: shared_ptr sont toujours sensibles aux fuites.
Jasper Bekkers

5
Seulement si vous les utilisez de manière incorrecte, même si je dois admettre que pour std :: auto_ptr, il est assez facile de l'utiliser de manière incorrecte.
Leon Timmermans

2
Bien que ce soit un bon conseil pour les normes de codage, cela ne répond pas à la question. Même l'utilisation de shared_ptr peut conduire à des fuites avec des dépendances circulaires. Et vous pouvez avoir des "fuites" avec des stratégies de mise en cache illimitées, qui s'appliquent même aux langages récupérés.
CashCow

@CashCow: vous avez raison. Bien que je ne l'ai pas encore vu en pratique, c'est probablement parce que je les utilise avec parcimonie. Pour citer la réponse ci-dessous, «N'utilisez des pointeurs que lorsque cela est absolument nécessaire».
Leon Timmermans

28

En tant que développeur C ++, voici quelques directives simples:

  1. N'utilisez des pointeurs que lorsque cela est absolument nécessaire
  2. Si vous avez besoin d'un pointeur, revérifiez si un SmartPointer est une possibilité
  3. Utilisez le modèle GRASP Creator .

En ce qui concerne la détection des fuites de mémoire personnellement, j'ai toujours utilisé Visual Leak Detector et je le trouve très utile.


2
Visual Leak Detectore déplacé vers le nouveau site vld.codeplex.com
KindDragon

VLD est un très bon détecteur de fuite - Je le recommande totalement à tous ceux qui utilisent VC ++
Javid

1
+1 pour le point # 1. C'est absolument la chose fondamentale. Malheureusement, il me semble que certaines des plus grandes bibliothèques "C ++" ont tendance à éviter l'allocation de pile et / ou RAII au profit de Pointers Everywhere, souvent sans raison discernable. Donc, ils finissent par être 'C avec classes', pas de vrai C ++.
underscore_d

16

J'utilise DevStudio depuis bien trop d'années maintenant et je suis toujours étonné de voir combien de programmeurs ne connaissent pas les outils d'analyse de la mémoire disponibles dans les bibliothèques d'exécution de débogage. Voici quelques liens pour commencer:

Suivi des demandes d'allocation de tas - en particulier la section sur les numéros de demande d'allocation uniques

_CrtSetDbgFlag

_CrtSetBreakAlloc

Bien sûr, si vous n'utilisez pas DevStudio, cela ne sera pas particulièrement utile.



7

Visual Leak Detector est un très bon outil, même s'il ne prend pas en charge les appels sur les runtimes VC9 (MSVCR90D.DLL par exemple).


1
Cet outil est vraiment parfait! Cela vous évite d'avoir à utiliser le _CrtDumpMemoryLeaks (); et amis, comme décrit dans MSDN. Juste un inclure et il expose tout! Fonctionne même dans les anciennes bibliothèques C!
m_pGladiator

La nouvelle version (pour VS2013) est ici: vld.codeplex.com
Dženan

7

Microsoft VC ++ en mode débogage montre les fuites de mémoire, bien qu'il n'indique pas où se trouvent vos fuites.

Si vous utilisez C ++ , vous pouvez toujours éviter d' utiliser explicitement nouvelle: vous avez vector, string, auto_ptr(pré C ++ 11, remplacé par unique_ptren C ++ 11), unique_ptr(C ++ 11) et shared_ptr(C ++ 11) dans votre arsenal.

Quand nouveau est inévitable, essayez de le cacher dans un constructeur (et de masquer la suppression dans un destructeur); la même chose fonctionne pour les API tierces.


1
et n'oubliez pas la règle de 3 ou de 5 alors
Humam Helfawi

4

Il existe diverses bibliothèques de remplacement "malloc" qui vous permettront d'appeler une fonction à la fin et qui vous renseignera sur toute la mémoire non libérée, et dans de nombreux cas, qui l'a mallocée (ou créée) en premier lieu .


4

Si vous utilisez MS VC ++, je vous recommande vivement cet outil gratuit du projet de code : leakfastinder de Jochen Kalmbach.

Vous ajoutez simplement la classe à votre projet et appelez

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

avant et après le code que vous souhaitez vérifier les fuites.

Une fois que vous avez créé et exécuté le code, Jochen fournit un outil graphique soigné dans lequel vous pouvez charger le fichier .xmlleaks résultant et naviguer dans la pile d'appels où chaque fuite a été générée pour rechercher la ligne de code incriminée.

PurifyPlus de Rational (maintenant détenu par IBM) illustre les fuites de la même manière, mais je trouve l'outil leakinder en fait plus facile à utiliser, avec l'avantage de ne pas coûter plusieurs milliers de dollars!


1
J'ai vérifié leakinder et ça a l'air bien, mais juste pour info, cela ne fonctionnera pas tel quel pour x64 car il contient un assemblage en ligne.
Zach le


3

Si vous utilisez Visual Studio, il peut être utile de consulter Bounds Checker . Ce n'est pas gratuit, mais cela a été incroyablement utile pour trouver des fuites dans mon code. Cela ne fait pas seulement des fuites de mémoire, mais aussi des fuites de ressources GDI, des erreurs d'utilisation de WinAPI et d'autres choses. Il vous montrera même où la mémoire divulguée a été initialisée, ce qui facilite grandement la recherche de la fuite.


2

Je pense qu'il n'y a pas de réponse facile à cette question. La manière dont vous pourriez vraiment aborder cette solution dépend de vos besoins. Avez-vous besoin d'une solution multiplateforme? Utilisez-vous new / delete ou malloc / free (ou les deux)? Êtes-vous vraiment à la recherche de "fuites" ou voulez-vous une meilleure protection, comme la détection de dépassements de mémoire tampon (ou de sous-utilisation)?

Si vous travaillez du côté Windows, les bibliothèques d'exécution de débogage MS ont des fonctionnalités de détection de débogage de base, et comme un autre l'a déjà souligné, plusieurs wrappers peuvent être inclus dans votre source pour aider à la détection des fuites. Trouver un package qui peut fonctionner à la fois avec new / delete et malloc / free vous donne évidemment plus de flexibilité.

Je ne connais pas assez le côté Unix pour fournir de l'aide, même si, encore une fois, d'autres l'ont.

Mais au-delà de la simple détection de fuite, il y a la notion de détection de la corruption de la mémoire via des dépassements de tampon (ou des sous-exécutions). Ce type de fonctionnalité de débogage est, à mon avis, plus difficile que la simple détection de fuite. Ce type de système est également plus compliqué si vous travaillez avec des objets C ++ car les classes polymorhpiques peuvent être supprimées de différentes manières, ce qui complique la détermination du véritable pointeur de base à supprimer. Je ne connais aucun bon système «gratuit» qui assure une protection décente contre les dépassements. nous avons écrit un système (multiplateforme) et nous l'avons trouvé assez difficile.


2

J'aimerais offrir quelque chose que j'ai parfois utilisé dans le passé: un vérificateur de fuite rudimentaire qui est au niveau de la source et assez automatique. Je donne cela pour trois raisons:

  1. Vous pourriez trouver cela utile.

  2. Bien que ce soit un peu krufty, je ne laisse pas cela me gêner.

  3. Même s'il est lié à certains hooks win32, cela devrait être facile à atténuer.

Il y a des choses auxquelles vous devez faire attention lorsque vous l'utilisez: ne faites rien sur lequel vous devez vous appuyer new dans le code sous-jacent, méfiez-vous des avertissements sur les cas qu'il pourrait manquer en haut de leakcheck.cpp, réalisez que si vous activez sur (et résoudre tous les problèmes avec) le code qui effectue des vidages d'image, vous pouvez générer un fichier énorme.

La conception est destinée à vous permettre d'activer et de désactiver le vérificateur sans recompiler tout ce qui comprend son en-tête. Incluez leakcheck.h où vous souhaitez suivre la vérification et reconstruire une fois. Ensuite, compilez le fichier leakcheck.cpp avec ou sans LEAKCHECK # défini, puis reliez-le pour l'activer et le désactiver. L'inclusion de unleakcheck.h le désactivera localement dans un fichier. Deux macros sont fournies: CLEARALLOCINFO () évitera de signaler le même fichier et la même ligne de manière inappropriée lorsque vous parcourez du code d'allocation qui n'inclut pas leakcheck.h. ALLOCFENCE () supprime simplement une ligne dans le rapport généré sans faire aucune allocation.

Encore une fois, sachez que je ne l'ai pas utilisé depuis un moment et que vous devrez peut-être travailler un peu avec. Je le dépose pour illustrer l'idée. S'il s'avère que l'intérêt est suffisant, je serais prêt à élaborer un exemple, à mettre à jour le code dans le processus et à remplacer le contenu de l'URL suivante par quelque chose de plus agréable comprenant une liste de syntaxe décemment colorée.

Vous pouvez le trouver ici: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html


2

Pour Linux: essayez Google Perftools

Il existe de nombreux outils qui font des allocations / comptages gratuits similaires, les avantages de Goolge Perftools:

  • Assez rapide (par rapport à valgrind: très rapide)
  • Livré avec un bel affichage graphique des résultats
  • Possède d'autres fonctionnalités utiles: profilage cpu, profilage d'utilisation de la mémoire ...


2

La meilleure défense contre les fuites est une structure de programme qui minimise l'utilisation de malloc. Ce n'est pas seulement bon du point de vue de la programmation, mais améliore également les performances et la maintenabilité. Je ne parle pas d'utiliser d'autres choses à la place de malloc, mais en termes de réutilisation des objets et de garder des onglets très explicites sur tous les objets en cours de transmission plutôt que d'allouer bon gré mal gré comme on s'habitue souvent dans les langues avec les ramasseurs d'ordures comme Java.

Par exemple, un programme sur lequel je travaille a un tas d'objets frame représentant des données d'image. Chaque objet frame a des sous-données, que le destructeur du frame libère. Le programme garde une liste de toutes les trames allouées, et quand il en a besoin d'une nouvelle, vérifie une liste d'objets de trame inutilisés pour voir s'il peut réutiliser une trame existante plutôt que d'en allouer une nouvelle. À l'arrêt, il itère simplement dans la liste, libérant tout.


2

Je recommanderais d'utiliser Memory Validator partir de la vérification du logiciel. Cet outil s'est avéré être d'une aide précieuse pour m'aider à détecter les fuites de mémoire et à améliorer la gestion de la mémoire des applications sur lesquelles je travaille.

Un outil très complet et rapide.


Memory Validator fournit également le nom de fichier et le numéro de ligne pour C # qui appelle votre code natif. La version x64 est en version bêta
Stephen Kellett

2

Comptez-vous les allocs et les libérations en interpolant vos propres fonctions syscall qui enregistrent les appels puis passent l'appel à la fonction réelle?

C'est la seule façon de garder une trace des appels provenant de code que vous n'avez pas écrit.

Jetez un œil à la page de manuel de ld.so. Ou ld.so.1 sur certains systèmes.

Faites également Google LD_PRELOAD et vous trouverez des articles intéressants expliquant la technique sur www.itworld.com.


1

Au moins pour MS VC ++, la bibliothèque C Runtime a plusieurs fonctions que j'ai trouvées utiles dans le passé. Consultez l'aide MSDN pour les _Crt*fonctions.


1

Le mmgr de Paul Nettle est un de mes outils préférés depuis longtemps. Vous incluez mmgr.h dans vos fichiers source, définissez TEST_MEMORY et il fournit un fichier texte plein de problèmes de mémoire survenus lors de l'exécution de votre application.


1

Directive générale de codage:

  • Les ressources doivent être désallouées sur la même "couche" (fonction / classe / bibliothèque) où elles sont allouées.
  • Si ce n'est pas possible, essayez d'utiliser une désallocation automatique (booster le pointeur partagé ...)

1

Les outils de débogage de la mémoire valent leur pesant d'or, mais au fil des ans, j'ai découvert que deux idées simples peuvent être utilisées pour empêcher la plupart des fuites de mémoire / ressources d'être codées en premier lieu.

  1. Écrivez le code de version immédiatement après avoir écrit le code d'acquisition pour les ressources que vous souhaitez allouer. Avec cette méthode, il est plus difficile d '«oublier» et, dans un certain sens, oblige à penser sérieusement au cycle de vie des ressources utilisées à l'avance plutôt que comme un aparté.

  2. Utilisez le retour avec parcimonie. Ce qui est alloué ne doit être libéré qu'à un seul endroit si possible. Le chemin conditionnel entre l'acquisition des ressources et la libération doit être conçu pour être aussi simple et évident que possible.


1

En haut de cette liste (quand je l'ai lu) se trouvait valgrind. Valgrind est excellent si vous êtes capable de reproduire la fuite sur un système de test. Je l'ai utilisé avec beaucoup de succès.

Et si vous venez de remarquer que le système de production fuit en ce moment et que vous ne savez pas comment le reproduire en test? Certaines preuves de ce qui ne va pas sont capturées dans l'état de ce système de production, et cela pourrait suffire à fournir un aperçu de l'emplacement du problème afin que vous puissiez le reproduire.

C'est là que l'échantillonnage Monte Carlo entre en scène. Lisez l'article du blog de Raymond Chen, «La manière du pauvre d'identifier les fuites de mémoire», puis consultez mon implémentation (suppose Linux, testé uniquement sur x86 et x86-64)

http://github.com/tialaramex/leakdice/tree/master


1

En travaillant sur le système d'exploitation des téléphones portables Motorola, nous avons détourné la bibliothèque d'allocation de mémoire pour observer toutes les allocations de mémoire. Cela a aidé à trouver de nombreux problèmes d'allocation de mémoire. Étant donné que la prévention vaut mieux que le durcissement, je recommanderais d'utiliser un outil d'analyse statique comme Klockwork ou PC-Lint


l'attelle est un remplacement plus récent pour les peluches.
Mark Kegel

@ user14788: Le produit PC-Lint de Gimpel est beaucoup plus moderne que l'ancien Unix lint. Il a de nombreux contrôles spécifiques au C ++, ce qu'afaik splint n'a pas. Voir le lien dans la réponse (que j'ai renommé de Lint en PC-Lint).
Dan

0

Valgrind est une bonne option pour Linux. Sous MacOS X, vous pouvez activer la bibliothèque MallocDebug qui a plusieurs options pour déboguer les problèmes d'allocation de mémoire (voir la page de manuel malloc, la section "ENVIRONNEMENT" contient les détails pertinents). Le SDK OS X comprend également un outil appelé MallocDebug (généralement installé dans / Developer / Applications / Performance Tools /) qui peut vous aider à surveiller l'utilisation et les fuites.


0

Détecter:

Déboguer CRT

Éviter:

Pointeurs intelligents, boehm GC


0

Un bon remplacement de malloc, calloc et reallloc est rmdebug, c'est assez simple à utiliser. Il est beaucoup plus rapide de valgrind, vous pouvez donc tester votre code de manière approfondie. Bien sûr, cela a quelques inconvénients, une fois que vous avez trouvé une fuite, vous devez probablement toujours utiliser valgrind pour trouver où la fuite apparaît et vous ne pouvez tester que les mallocs que vous faites directement. Si une bibliothèque fuit parce que vous l'utilisez mal, rmdebug ne la trouvera pas.

http://www.hexco.de/rmdebug/


0

La plupart des profileurs de mémoire ralentissent ma grande application Windows complexe au point que les résultats sont inutiles. Il existe un outil qui fonctionne bien pour rechercher des fuites dans mon application: UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx


Je ne vois pas pourquoi le ralentissement rend les résultats inutiles. La mémoire qui est perdue est sûrement perdue quelle que soit la vitesse à laquelle le programme s'exécute. Le but de ces outils est de trouver des fuites, alors où est le problème? Cela s'est-il déroulé si lentement que vous ne pouviez pas physiquement le faire couvrir tous les chemins de code afin de les profiler?
underscore_d

-1

Mtrace semble être le standard intégré pour Linux. Les étapes sont:

  1. configurer la variable d'environnement MALLOC_TRACE dans bash
    MALLOC_TRACE = / tmp / mtrace.dat
    export MALLOC_TRACE;
  2. Ajoutez #include <mcheck.h> en haut de votre fichier source principal
  3. Ajoutez mtrace (); au début de main et muntrace (); en bas (avant l'instruction de retour)
  4. compilez votre programme avec le commutateur -g pour les informations de débogage
  5. lancez votre programme
  6. afficher les informations de fuite avec
    mtrace your_prog_exe_name /tmp/mtrace.dat
    (j'ai d'abord dû installer le script perl mtrace sur mon système fedora avec yum install glibc_utils   )

mtrace n'est pas très utile pour C ++
Erin
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.