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.
- Je veux savoir comment le programmeur peut trouver des fuites de mémoire.
- 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?
c++
memory-leaks
Chris_vr
la source
la source
Réponses:
Instructions
Les choses dont vous aurez besoin
1
Comprenez les bases de l'opérateur. L'opérateur C ++
new
alloue la mémoire du tas. L'delete
opérateur libère la mémoire du tas. Pour toutnew
, vous devez utiliser undelete
afin de libérer la même mémoire que vous avez allouée:2
Réallouez la mémoire uniquement si vous avez supprimé. Dans le code ci-dessous,
str
acquiert 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: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:
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:
5
Faites attention aux accolades après «supprimer». À utiliser
delete
seul pour libérer un seul objet. Utilisezdelete []
avec des crochets pour libérer un tableau de tas. Ne faites pas quelque chose comme ça:6
Si la fuite est encore autorisée - je la recherche généralement avec deleaker (vérifiez-la ici: http://deleaker.com ).
la source
someFunction("some parameter")
dois-je supprimer"some parameter"
dans lesomeFunction
, après l'appel de fonction, ou sont-ils automatiquement supprimés?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:
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 ,
la source
#define
gâchera la surchargeoperator new
et 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.auto_ptr
ne fonctionnera pas avec les conteneurs standard tels questd::vector
,std::list
etc. Voir ceci: stackoverflow.com/questions/111478/…operator new
ce que c'est et quelles sont ces versions que vous utilisez?Il existe des techniques de programmation bien connues qui vous aideront à minimiser le risque de fuites de mémoire de première main:
new
etdelete
toujours par paires, et assurez-vous que le code d'allocation / désallocation est appelé par pairesvector<T> t
autant que possible au lieu deT* t = new T[size]
la source
Valgrind http://valgrind.org/
et
GDB http://www.gnu.org/software/gdb/
la source
gflags
utilitaire pour activer les traces de pile en mode utilisateur.UMDH
pour 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écuterUMDH
et de prendre les instantanés.UMDH
nouveau, 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.gflags
paramètres précédents lorsque vous avez terminé.UMDH
vous 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.la source
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
-g
drapeau3) Dans votre exécution de shell:
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
/new
qui n'ont pas eu d'appels ultérieurs à supprimer gratuitement.Par exemple:
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 appeldelete
/ suivantfree
.la source
Si vous utilisez gcc, gprof est disponible.
Certains utilisent des outils, certains font ce que vous faites, peuvent également être examinés par des pairs
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 ++).
la source
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.
Ce forum aborde quelques moyens d'éviter les fuites de mémoire en C / C ++.
la source
Recherchez dans votre code les occurrences de
new
et 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 pointeursstd::auto_ptr
, ouboost::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.la source
scope_ptr
s 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.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
la source
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
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:
La sortie de l'exécution du programme contient l'analyse des fuites de mémoire:
et la sortie de
google-pprof
contient l'analyse de l'utilisation du tas:La sortie nous indique deux des trois fuites:
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:
et sorties d'exécution:
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:
Production:
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.
la source
Visual Leak Detector (VLD) est un système de détection de fuite de mémoire libre, robuste et open source pour Visual C ++.
Si vous n'avez que des vidages sur incident, vous pouvez utiliser la
!heap -l
commande 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.la source
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:
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.
la source
Sous Windows, vous pouvez utiliser le tas de débogage CRT .
Ouais, n'utilisez pas la gestion manuelle de la mémoire (si jamais vous appelez
delete
oudelete[]
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).la source
Répondant à la deuxième partie de votre question,
Oui il y a. Et c'est l'une des principales différences entre C et C ++.
En C ++, vous ne devez jamais appeler
new
nidelete
dans 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ù:new
si la ressource est une allocation memroy)Cette classe est ensuite instanciée localement, sur la pile ou en tant que membre de classe, et non en appelant
new
et 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::vector
est libéré lorsque le vecteur est détruit. Encore une fois, ne stockez pas de pointeur dans le conteneur (ce qui vous obligerait à appelernew
etdelete
), 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ésnew
et 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é.
la source
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:
Cet outil est très rapide. Le ralentissement moyen du programme instrumenté est de ~ 2x.
la source
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.
la source
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.
la source
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.
la source