Fuites mémoire et analyse avec valgrind
Détecter les fuites, lire les rapports valgrind et éviter les pièges d’allocation.
En C++, la gestion dynamique de la mémoire offre une grande flexibilité au développeur, mais une mauvaise utilisation de la mémoire peut entraîner des fuites de mémoire (memory leaks). Une fuite de mémoire se produit lorsqu’une zone de mémoire allouée n’est pas libérée, ce qui finit par saturer la RAM et dégrader les performances du programme. Dans cet article, nous verrons ce qu’est une fuite de mémoire, comment la détecter et comment l’analyser avec l’outil Valgrind.
1. Qu’est-ce qu’une fuite de mémoire ?
Une fuite de mémoire survient lorsqu’un bloc de mémoire alloué avec new ou malloc
n’est pas libéré avec delete ou free.
Dans ce cas, la mémoire reste occupée jusqu’à la fin du programme et ne peut pas être utilisée par d’autres processus.
#include <iostream>
using namespace std;
void CreerFuite() {
int *p = new int(42); // Mémoire dynamique allouée
// delete p; // Oubliée !
}
int main() {
CreerFuite();
cout << "Programme terminé." << endl;
}
Dans cet exemple, la mémoire allouée à p reste dans la RAM jusqu’à la fin du programme.
Même après la fermeture du programme, cette mémoire est « perdue » jusqu’à ce que le système d’exploitation la récupère.
Ce type de problème peut provoquer de graves erreurs dans les applications serveur de longue durée.
2. Conséquences d’une fuite de mémoire
- L’application consomme de plus en plus de RAM au fil du temps.
- Les performances diminuent et l’exécution ralentit.
- Les systèmes fonctionnant longtemps peuvent finir par planter.
- Dans les systèmes embarqués, la mémoire limitée peut provoquer un blocage complet.
3. Situations typiques provoquant une fuite
- Oubli de delete ou delete[]
- Réécriture d’un pointeur (perte de l’adresse d’origine)
- Retour prématuré d’une fonction sans libération
deletenon exécuté en cas d’exception- Mauvaise gestion du cycle de vie des pointeurs possédant des objets dynamiques
void CodeIncorrect() {
int *p = new int[5];
p = new int[10]; // Ancienne adresse perdue → fuite de 5 int
delete[] p;
}
4. Comment éviter les fuites de mémoire ?
Quelques bonnes pratiques permettent d’éviter les fuites de mémoire :
- Chaque
newdoit avoir undeletecorrespondant. - Chaque
new[]doit avoir undelete[]correspondant. - Après la libération d’un pointeur, le remettre à
nullptr. - Appliquer le principe RAII (Resource Acquisition Is Initialization).
- Utiliser des pointeurs intelligents en C++ moderne.
#include <iostream>
#include <memory>
using namespace std;
void UtilisationSûre() {
auto ptr = make_unique<int>(99);
cout << *ptr << endl;
} // ptr est automatiquement libéré en sortant de la portée
Dans cet exemple, grâce à std::unique_ptr, il n’est pas nécessaire d’appeler delete manuellement :
la mémoire est libérée automatiquement.
5. Détection des fuites de mémoire (Valgrind)
Valgrind est un outil open-source d’analyse de mémoire fonctionnant sous Linux. Il surveille tous les blocs de mémoire alloués et libérés pendant l’exécution du programme et signale les fuites, accès invalides ou doubles libérations.
Installation (Ubuntu / Debian)
sudo apt update
sudo apt install valgrind
Utilisation
Après la compilation, vous pouvez exécuter votre programme avec Valgrind comme suit :
g++ -g programme.cpp -o programme
valgrind --leak-check=full ./programme
L’option -g ajoute les symboles de débogage,
permettant à Valgrind d’afficher les numéros de ligne dans le rapport.
6. Exemple de sortie Valgrind
Supposons que le programme suivant provoque une fuite :
#include <iostream>
using namespace std;
int main() {
int *p = new int[3];
p[0] = 10;
p[1] = 20;
p[2] = 30;
// delete[] p; // Oubliée !
return 0;
}
La sortie Valgrind serait :
==1234== 12 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1234== at 0x4C2FB55: operator new[](unsigned long) (vg_replace_malloc.c:431)
==1234== by 0x40065A: main (programme.cpp:5)
==1234== LEAK SUMMARY:
==1234== definitely lost: 12 bytes in 1 blocks
Ce rapport indique que 12 octets (3 × int) de mémoire n’ont pas été libérés. Grâce au numéro de ligne, il est facile de repérer la source de l’erreur.
7. Autres options utiles de Valgrind
| Option | Description |
|---|---|
--track-origins=yes | Indique l’origine des valeurs non initialisées. |
--show-leak-kinds=all | Affiche toutes les catégories de fuites (definitely, indirectly). |
--num-callers=20 | Affiche une pile d’appels plus détaillée. |
--log-file=valgrind.log | Enregistre les résultats dans un fichier. |
8. Outils alternatifs sous Windows
- Visual Studio Diagnostic Tools → Fournit une analyse de la mémoire et des fuites.
- Dr. Memory → Outil gratuit similaire à Valgrind pour Windows.
- Deleaker → Extension IDE fournissant des rapports de mémoire détaillés.
9. Bonnes pratiques
- Écrire un
deletepour chaquenew. - Réinitialiser les pointeurs à
nullptraprès libération. - Ne jamais faire pointer plusieurs pointeurs vers la même adresse (risque de double suppression).
- Utiliser RAII pour éviter les fuites de ressources lors des exceptions.
- Utiliser des pointeurs intelligents (ex.
std::unique_ptr) pour éliminer complètement les fuites.
10. TL;DR
- Fuite de mémoire = mémoire allouée avec
newmais non libérée avecdelete. - Valgrind est un outil puissant pour détecter les fuites de mémoire.
- Testez votre programme avec
valgrind --leak-check=full ./programme. - En C++ moderne, il est recommandé d’utiliser
std::unique_ptroustd::shared_ptr. - Sous Windows, utilisez Visual Studio Diagnostic Tools ou Dr. Memory.
- Tous les exemples peuvent être exécutés avec Visual Studio 2022 ou GCC 11+.