web-dev-qa-db-fra.com

Est-ce que les fuites de mémoire sont correctes?

Est-il jamais acceptable d'avoir une fuite de mémoire dans votre application C ou C++?

Et si vous allouez de la mémoire et que vous l’utilisez jusqu’à la toute dernière ligne de code de votre application (par exemple, le destructeur d’un objet global)? Tant que la consommation de mémoire ne croît pas avec le temps, est-il possible de faire confiance au système d'exploitation pour libérer votre mémoire lorsque l'application se termine (sous Windows, Mac et Linux)? Considérez-vous cela comme une véritable fuite de mémoire si la mémoire était utilisée en permanence jusqu'à sa libération par le système d'exploitation?.

Et si une bibliothèque tierce vous forçait de force? Refuserait-il d'utiliser cette bibliothèque tierce, peu importe sa taille?

Je ne vois qu'un inconvénient pratique, à savoir que ces fuites bénignes apparaîtront comme des faux positifs avec les outils de détection des fuites de mémoire.

214
Imbue

Non.

En tant que professionnels, la question que nous ne devrions pas nous poser est la suivante: "Est-il toujours acceptable de faire cela?" mais plutôt "Y a-t-il jamais une bonne raison de le faire?" Et "traquer cette fuite de mémoire est une douleur" n’est pas une bonne raison.

J'aime garder les choses simples. Et la règle simple est que mon programme ne devrait avoir aucune fuite de mémoire.

Cela me simplifie la vie aussi. Si je détecte une fuite de mémoire, je l'élimine au lieu d'analyser une arborescence de décision élaborée pour déterminer s'il s'agit d'une fuite de mémoire "acceptable".

C'est semblable aux avertissements du compilateur - l'avertissement sera-t-il fatal à mon application particulière? Peut être pas. 

Mais c'est finalement une question de discipline professionnelle. Tolérer les avertissements du compilateur et les fuites de mémoire est une mauvaise habitude qui finira par me mordre à l’arrière.

Pour aller plus loin, serait-il jamais acceptable pour un chirurgien de laisser du matériel d'opération à l'intérieur d'un patient?

Bien qu'il soit possible que le coût/risque de retirer cet équipement dépasse le coût/risque de le laisser dedans, il peut arriver qu'il soit inoffensif si je voyais cette question postée sur SurgeonOverflow.com et a vu une réponse autre que "non", cela minerait sérieusement ma confiance en la profession médicale.

-

Si une bibliothèque tierce m'avait imposé la situation, cela me conduirait à suspecter sérieusement la qualité globale de la bibliothèque en question. Ce serait comme si je conduisais une voiture et que je trouvais quelques rondelles et des écrous dans l’un des porte-gobelets - ce n’est peut-être pas grave, mais cela dénote un manque d’engagement envers la qualité.

317
JohnMcG

Je ne considère pas qu'il s'agisse d'une fuite de mémoire, sauf si la quantité de mémoire "utilisée" continue de croître. Avoir de la mémoire non libérée, même s’il n’est pas idéal, n’est pas un gros problème, à moins que la quantité de mémoire nécessaire ne cesse de croître.

80
Jim C

Commençons par nos définitions. Une mémoire fuite se produit lorsque la mémoire est allouée dynamiquement, par exemple avec malloc(), et que toutes les références à la mémoire sont perdues sans la réserve libre correspondante. Un moyen facile d'en créer un est comme ceci:

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

Notez que chaque fois autour de la boucle while (1), 1024 octets (en plus) sont alloués et la nouvelle adresse est attribuée à vp; il ne reste aucun pointeur sur les blocs Malloc'ed précédents. Il est garanti que ce programme fonctionnera jusqu’à épuisement du tas et qu’il n’ya aucun moyen de récupérer la mémoire de malloc’ed. La mémoire «fuit» du tas, pour ne plus jamais être vue.

Ce que vous décrivez, cependant, sonne comme

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

Vous allouez la mémoire, travaillez avec elle jusqu'à la fin du programme. C'est pas une fuite de mémoire; cela n'affecte pas le programme, et toute la mémoire sera effacée automatiquement lorsque le programme se terminera.

En règle générale, vous devriez éviter les fuites de mémoire. Premièrement, parce que, tout comme l’altitude au-dessus de vous et le ravitaillement au hangar, la mémoire qui a fui et qui ne peut pas être récupérée est inutile; deuxièmement, il est beaucoup plus facile de coder correctement, de ne pas laisser de mémoire, au début que de rechercher une fuite de mémoire plus tard.

77
Charlie Martin

En théorie non, en pratique cela dépend .

Cela dépend vraiment de la quantité de données sur laquelle le programme travaille, de sa fréquence d'exécution et de son exécution constante ou non.

Si j'ai un programme rapide qui lit une petite quantité de données, effectue un calcul et se ferme, une petite fuite de mémoire ne sera jamais remarquée. Parce que le programme ne fonctionne pas très longtemps et utilise seulement une petite quantité de mémoire, la fuite sera petite et libérée lorsque le programme existe.

Par ailleurs, si j’ai un programme qui traite des millions d’enregistrements et s’exécute sur une longue période, une petite fuite de mémoire peut entraîner un ralentissement de la machine.

En ce qui concerne les bibliothèques tierces présentant des fuites, si elles posent un problème, corrigez-les ou recherchez une meilleure alternative. Si cela ne pose pas de problème, est-ce vraiment important?

39
vfilby

De nombreuses personnes semblent avoir l’impression qu’une fois la mémoire libérée, elle est instantanément restituée au système d’exploitation et peut être utilisée par d’autres programmes.

Ce n'est pas vrai Les systèmes d'exploitation gèrent généralement la mémoire dans des pages de 4 Ko. malloc et d’autres types de gestion de la mémoire récupèrent les pages du système d’exploitation et les sous-gèrent à leur guise. Il est fort probable que free() retournera non pages au système d'exploitation, en supposant que votre programme utilisera plus de mémoire plus tard.

Je ne dis pas que free() ne renvoie jamais de mémoire au système d'exploitation. Cela peut arriver, surtout si vous libérez de grandes quantités de mémoire. Mais il n'y a aucune garantie.

Le fait important: Si vous ne libérez pas de la mémoire dont vous n'avez plus besoin, il est garanti que d'autres mallocs consomment encore plus de la mémoire. Mais si vous libérez d'abord, malloc pourrait réutiliser la mémoire libérée.

Qu'est-ce que cela signifie en pratique? Cela signifie que si vous savez que votre programme ne nécessitera plus de mémoire à partir de maintenant (par exemple, il est en phase de nettoyage), la libération de mémoire n'est pas si importante. Toutefois, si le programme peut allouer plus de mémoire ultérieurement, vous devez éviter les fuites de mémoire, en particulier celles qui peuvent se produire à plusieurs reprises.

Voir aussi ce commentaire pour plus de détails sur les raisons pour lesquelles libérer de la mémoire juste avant la fin est mauvais.

Un intervenant n'a pas semblé comprendre que l'appel de free() n'autorisait pas automatiquement les autres programmes à utiliser la mémoire libérée. Mais c'est tout l'intérêt de cette réponse!

Donc, pour convaincre les gens, je vais vous montrer un exemple où free () fait très peu de bien. Pour faciliter le calcul, je vais prétendre que le système d'exploitation gère la mémoire sur des pages de 4000 octets.

Supposons que vous allouiez dix mille blocs de 100 octets (pour des raisons de simplicité, j'ignorerai la quantité de mémoire supplémentaire nécessaire pour gérer ces allocations). Cela consomme 1 Mo, ou 250 pages. Si vous libérez ensuite 9 000 de ces blocs au hasard, il ne vous reste que 1 000 blocs, mais ils sont dispersés un peu partout. Statistiquement, environ 5 pages seront vides. Les 245 autres auront chacun au moins un bloc attribué. Cela équivaut à 980 Ko de mémoire, qui ne peut éventuellement pas être récupéré par le système d'exploitation - même si vous ne disposez maintenant que de 100 Ko alloués!

D'autre part, vous pouvez maintenant malloc () 9000 blocs supplémentaires sans augmenter la quantité de mémoire allouée par votre programme.

Même lorsque free() pourrait techniquement} restituer de la mémoire au système d'exploitation, il se peut que ce ne soit pas le cas. free() doit parvenir à un équilibre entre fonctionnement rapide et économie de mémoire. De plus, un programme qui a déjà alloué beaucoup de mémoire, puis libéré, est susceptible de le faire à nouveau. Un serveur Web doit gérer requête après demande, requête après requête. Il est donc logique de garder de la mémoire "vacante" disponible afin de ne pas avoir à demander de mémoire au système d'exploitation tout le temps.

35
Artelius

Il n’ya rien de mal à ce que l’os ait été nettoyé une fois l’application lancée.

Cela dépend vraiment de l'application et de la manière dont elle sera exécutée. Les fuites qui se produisent continuellement dans une application qui doit fonctionner pendant des semaines doivent être résolues, mais un petit outil qui calcule un résultat sans nécessiter trop de mémoire ne devrait pas poser de problème.

Il y a une raison pour laquelle de nombreux langages de script ne collectent pas de mémoire de déchets cyclique… pour leurs habitudes d'utilisation, ce n'est pas un problème réel et serait donc autant un gaspillage de ressources que la mémoire perdue.

27
kasperjj

Je crois que la réponse est non, n'autorisez jamais une fuite de mémoire et j'ai quelques raisons que je n'ai pas vues explicitement. Il y a d'excellentes réponses techniques ici, mais je pense que la vraie réponse repose sur davantage de raisons sociales/humaines.

(Tout d’abord, notez que, comme d’autres l'ont mentionné, une véritable fuite survient lorsque votre programme perd à tout moment la trace des ressources de mémoire qu'il a allouées. En C, cela se produit lorsque vous malloc() et que vous laissez ce pointeur quitter la portée sans le faire free() en premier.)

Le noeud important de votre décision ici est l'habitude. Lorsque vous codez dans une langue qui utilise des pointeurs, vous allez utiliser des pointeurs beaucoup. Et les pointeurs sont dangereux. c'est le moyen le plus simple d'ajouter toutes sortes de problèmes graves à votre code.

Lorsque vous codez, vous risquez parfois de perdre votre temps et d’être fatigué, furieux ou inquiet. Pendant cette période quelque peu distraite, vous codez davantage sur le pilote automatique. L'effet de pilote automatique ne fait pas la différence entre un code unique et un module dans un projet plus grand. Pendant ces périodes, les habitudes que vous établissez sont ce qui se retrouvera dans votre base de code.

Donc, non, n'autorisez jamais les fuites de mémoire pour la même raison que vous devriez quand même vérifier vos angles morts lorsque vous changez de voie, même si vous êtes la seule voiture sur la route pour le moment. Pendant les moments où votre cerveau actif est distrait, de bonnes habitudes sont tout ce qui peut vous sauver des faux pas désastreux.

Au-delà du problème "d'habitude", les pointeurs sont complexes et nécessitent souvent beaucoup de puissance cérébrale pour être suivis mentalement. Il est préférable de ne pas "brouiller les pistes" en ce qui concerne votre utilisation des pointeurs, en particulier lorsque vous débutez dans la programmation.

Il y a aussi un aspect plus social. Si vous utilisez correctement les fonctions malloc() et free(), tous ceux qui consultent votre code seront à l'aise. vous gérez vos ressources. Si vous ne le faites pas, ils suspecteront immédiatement un problème.

Peut-être que vous avez compris que la fuite de mémoire ne faisait aucun mal dans ce contexte, mais chaque mainteneur de votre code devra en tenir compte dans sa tête également lorsqu'il lira ce morceau de code. En utilisant free(), vous évitez de prendre en compte le problème.

Enfin, la programmation consiste à écrire un modèle mental de processus dans un langage non ambigu, de sorte qu'une personne et un ordinateur puissent parfaitement comprendre ce processus. Un élément essentiel d'une bonne pratique de programmation est de ne jamais introduire d'ambiguïté inutile.

La programmation intelligente est flexible et générique. Une mauvaise programmation est ambiguë.

19
Jason L

Je pense que dans votre cas, la réponse pourrait être que tout va bien. Mais vous devez absolument documenter que la fuite de mémoire est une décision consciente. Vous ne voulez pas qu'un programmeur de maintenance se présente, insère votre code dans une fonction et l'appelle un million de fois. Donc, si vous décidez qu'une fuite est acceptable, vous devez la documenter (EN GRANDES LETTRES) pour quiconque devra travailler sur le programme à l'avenir.

S'il s'agit d'une bibliothèque tierce, vous pouvez être pris au piège. Mais clairement documenter que cette fuite se produit.

Mais fondamentalement, si la fuite de mémoire est une quantité connue, telle qu’une mémoire tampon de 512 Ko ou quelque chose du genre, il n’ya pas de problème. Si la fuite de mémoire ne cesse de croître, comme chaque fois que vous appelez une bibliothèque, votre mémoire augmente de 512 Ko et n'est pas libérée, il est possible que vous rencontriez un problème. Si vous le documentez et contrôlez le nombre de fois que l'appel est exécuté, il peut être gérable. Mais alors, vous avez vraiment besoin de documentation car, bien que 512 ne soit pas beaucoup, 512 millions d'appels, c'est beaucoup.

Aussi, vous devez vérifier la documentation de votre système d'exploitation. S'il s'agissait d'un périphérique intégré, il est possible que certains systèmes d'exploitation ne libèrent pas toute la mémoire d'un programme existant. Je ne suis pas sûr, peut-être que ce n'est pas vrai. Mais cela vaut la peine de regarder.

15
Cervo

Je vais donner la réponse impopulaire mais pratique selon laquelle il est toujours erroné de libérer de la mémoire, à moins que cela ne réduise l'utilisation de la mémoire par votre programme . Par exemple, un programme qui effectue une seule allocation ou une série d'allocations pour charger l'ensemble de données qu'il utilisera pendant toute sa durée de vie n'a pas besoin de rien libérer. Dans le cas le plus courant d’un programme volumineux avec des besoins en mémoire très dynamiques (pensez à un navigateur Web), vous devez évidemment libérer la mémoire que vous n’utilisez plus dès que vous le pouvez (par exemple en fermant un onglet/document/etc.). , mais il n’ya aucune raison de libérer quoi que ce soit lorsque l’utilisateur sélectionne les clics "quitter", ce qui est préjudiciable à son expérience.

Pourquoi? Libérer de la mémoire nécessite de toucher à la mémoire. Même si l'implémentation malloc de votre système ne stocke pas les métadonnées adjacentes aux blocs de mémoire alloués, vous allez probablement parcourir des structures récursives uniquement pour trouver tous les pointeurs que vous devez libérer.

Supposons maintenant que votre programme a fonctionné avec un grand volume de données, mais qu’il n’en a pas touché depuis un moment (le navigateur Web en est un excellent exemple). Si l'utilisateur exécute de nombreuses applications, une bonne partie de ces données a probablement été échangée sur le disque. Si vous venez de quitter (0) ou de revenir du principal, il se ferme instantanément. Grande expérience utilisateur. Si vous vous donnez la peine d'essayer de tout libérer, vous pouvez passer 5 secondes ou plus à échanger toutes les données, puis à les jeter immédiatement après cela. Perte de temps de l'utilisateur. Perte de la vie de la batterie de l'ordinateur portable. Déchets d'usure sur le disque dur.

Ce n'est pas que théorique. À chaque fois que je me trouve avec trop d'applications chargées et que le disque se met à battre, je ne songe même pas à cliquer sur "quitter". J'arrive à un terminal aussi vite que possible et je tue killall -9 ... parce que je sais que "exit" ne fera qu'empirer les choses.

14
R..

Je suis sûr que quelqu'un peut trouver une raison de dire Oui, mais ce ne sera pas moi… .. Au lieu de dire non, je vais dire que cela ne devrait pas être une question oui/non. Il existe des moyens de gérer ou de contenir les fuites de mémoire, et de nombreux systèmes en disposent.

Il y a des systèmes de la NASA sur des appareils qui laissent la terre qui se préparent à cela. Les systèmes redémarrent automatiquement de temps en temps afin que les fuites de mémoire ne deviennent pas fatales au fonctionnement général. Juste un exemple de confinement.

11
pearcewg

Si vous allouez de la mémoire et l'utilisez jusqu'à la dernière ligne de votre programme, il ne s'agit pas d'une fuite. Si vous allouez de la mémoire et l'oubliez, même si la quantité de mémoire n'augmente pas, c'est un problème. Cette mémoire allouée mais non utilisée peut ralentir les programmes ou ne pas les exécuter du tout.

8
Bill the Lizard

Je peux compter sur une main le nombre de fuites "bénignes" que j'ai vues au fil du temps.

Donc, la réponse est un oui très qualifié. 

Un exemple. Si vous avez une ressource singleton qui a besoin d'un tampon pour stocker une file d'attente circulaire ou un deque mais que vous ne connaissez pas la taille du tampon et ne peut vous permettre le surcoût du verrouillage ou de chaque lecteur, allouez un tampon doublant de manière exponentielle mais ne pas libérer les anciens entraînera une fuite de mémoire limitée par file d'attente/parque. L'avantage de ces solutions est qu'elles accélèrent considérablement chaque accès et qu'elles peuvent modifier les asymptotiques des solutions multiprocesseurs en ne risquant jamais une contention pour un verrou.

J'ai constaté que cette approche était très avantageuse pour des tâches dont le nombre était clairement défini, telles que les deques de vol par processeur, et dans une bien moindre mesure dans la mémoire tampon utilisée pour conserver l'état singleton /proc/self/maps dans le récupérateur de déchets conservateur de Hans Boehm. C/C++, utilisé pour détecter les ensembles de racines, etc.

Bien qu’il s’agisse techniquement d’une fuite, la taille de ces deux cas est limitée et, dans l’affaire pouvant être volée, il existe une énorme performance gagnée en contrepartie d’une augmentation du facteur 2 de la mémoire utilisée par les files .

8
Edward KMETT

Si vous allouez un tas de tas au début de votre programme et que vous ne le libérez pas lorsque vous quittez, ce n'est pas une fuite de mémoire en soi. Une fuite de mémoire survient lorsque votre programme parcourt une section de code et que ce code alloue le tas, puis en «perd la trace» sans le libérer.

En fait, il n'est pas nécessaire d'appeler free () ou de supprimer juste avant de quitter. Lorsque le processus se termine, toute la mémoire de celui-ci est récupérée par le système d'exploitation (c'est certainement le cas avec POSIX. Sur d'autres systèmes d'exploitation - notamment ceux intégrés - YMMV).

La seule précaution que je ferais pour ne pas libérer la mémoire au moment de la sortie est que, si vous refactorisez votre programme afin qu'il devienne, par exemple, un service qui attend une entrée, fait ce que votre programme fait, un autre appel de service, alors ce que vous avez codé peut se transformer en fuite mémoire.

8
nsayer

ceci est tellement spécifique à un domaine qu'il ne vaut guère la peine d'y répondre. Utilisez votre tête freaking.

  • système d'exploitation de la navette spatiale: Nope, aucune fuite de mémoire autorisée
  • code de validation de principe de développement rapide: réparer toutes ces fuites de mémoire est une perte de temps.

et il y a un éventail de situations intermédiaires.

le coût d'opportunité ($$$) de retarder la publication d'un produit pour corriger toutes les fuites de mémoire, sauf les pires, évite généralement tout sentiment d'être «négligé ou non professionnel». Votre patron vous paye pour lui faire de l'argent, pas pour avoir des sentiments chauds et confus.

6
Dustin Getz

Vous devez d’abord comprendre qu’il existe une grande différence entre une fuite de mémoire perçue et une fuite de mémoire réelle. Très souvent, les outils d’analyse signalent de nombreuses menaces rouges et signalent les fuites (mémoire ou ressources telles que des poignées, etc.) là où ce n’est pas le cas. Cela est souvent dû à l'architecture de l'outil d'analyse. Par exemple, certains outils d'analyse signaleront les objets d'exécution comme des fuites de mémoire car ils ne verront jamais ces objets libérés. Mais la désallocation se produit dans le code d'arrêt du moteur d'exécution, ce que l'outil d'analyse pourrait ne pas être en mesure de voir.

Cela dit, il y aura toujours des moments où vous aurez des fuites de mémoire qui sont soit très difficiles à trouver, soit très difficiles à réparer. Alors maintenant, la question devient: est-il toujours possible de les laisser dans le code?

La réponse idéale est: "non, jamais." Une réponse plus pragmatique pourrait être «non, presque jamais». Très souvent, dans la vie réelle, vous disposez de peu de ressources et de temps pour résoudre une liste de tâches sans fin. Lorsque l’une des tâches consiste à éliminer les fuites de mémoire, la loi des rendements décroissants entre souvent en jeu. Vous pourriez éliminer 98% de toutes les fuites de mémoire d'une application en une semaine, mais les 2% restants pourraient prendre des mois. Dans certains cas, il pourrait même être impossible d'éliminer certaines fuites en raison de l'architecture de l'application sans une refactorisation majeure du code. Il faut peser les coûts et les avantages de l'élimination des 2% restants.

5
John Dibling

Alors que la plupart des réponses se concentrent sur de véritables fuites de mémoire (qui ne sont jamais acceptables, car elles sont un signe de codage négligé), cette partie de la question me semble plus intéressante:

Que faire si vous allouez de la mémoire et l’utilisez jusqu’à la toute dernière ligne de code de votre application (par exemple, le déconstructeur d’un objet global)? Tant que la consommation de mémoire ne croît pas avec le temps, est-il possible de faire confiance au système d'exploitation pour libérer votre mémoire lorsque l'application se termine (sous Windows, Mac et Linux)? Considérez-vous cela comme une véritable fuite de mémoire si la mémoire était utilisée en permanence jusqu'à sa libération par le système d'exploitation?.

Si la mémoire associée est utilisée, vous ne pouvez pas la libérer avant la fin du programme. Que le libre soit effectué par la sortie du programme ou par le système d'exploitation n'a pas d'importance. Tant que cela est documenté, le changement n'entraîne pas de véritables fuites de mémoire, et tant qu'il n'y a pas de destructeur C++ ni de fonction de nettoyage C impliqués dans l'image. Un fichier non fermé peut être révélé via un objet FILE qui présente une fuite, mais une fonction fclose () manquante peut également empêcher le vidage de la mémoire tampon. 

Donc, revenons au cas original, il est à mon humble avis tout à fait acceptable, à tel point que Valgrind, l’un des détecteurs de fuites le plus puissant, ne traitera ces fuites que sur demande. Sous Valgrind, lorsque vous écrasez un pointeur sans le libérer au préalable, il est considéré comme une fuite de mémoire, car il est plus probable qu'il se reproduise et que le tas se développe sans cesse.

Ensuite, il n’ya pas de blocs mémoire libres qui sont toujours accessibles. On pourrait s'assurer de les libérer tous à la sortie, mais ce n'est qu'une perte de temps en soi. Le fait est qu'ils puissent être libérés avant . Réduire la consommation de mémoire est utile dans tous les cas.

5
Blaisorblade

Dans ce genre de question, tout est dans le contexte. Personnellement, je ne supporte pas les fuites, et dans mon code, je fais tout ce qui est en mon pouvoir pour les réparer si elles se présentent, mais cela ne vaut pas toujours la peine de réparer une fuite, et lorsque les gens me paient à l'heure qui m'est impartie leur a dit que cela ne valait pas la peine que je me charge de réparer une fuite dans leur code. Laisse moi te donner un exemple:

Je triais un projet, faisais quelques travaux et corrigeais beaucoup de bugs. Lors de l'initialisation des applications, il y a eu une fuite que j'ai repérée et que j'ai bien comprise. Pour le corriger correctement, il aurait fallu environ une journée pour refactoriser un morceau de code par ailleurs fonctionnel. J'aurais pu faire quelque chose de hacky (comme insérer de la valeur dans un fichier global et le récupérer, je sais qu'il n'était plus utilisé gratuitement), mais cela aurait simplement causé davantage de confusion au prochain utilisateur qui devait manipuler le code.

Personnellement, je n’aurais pas écrit le code de cette façon au départ, mais la plupart d’entre nous ne travaillons pas toujours sur des bases de code vierges et bien conçues, et il est parfois nécessaire de regarder ces choses de manière pragmatique. Le temps qu'il aurait fallu pour résoudre cette fuite de 150 octets pourrait être consacré à des améliorations algorithmiques qui réduisent les mégaoctets de RAM.

En fin de compte, j’ai décidé qu’une fuite de 150 octets pour une application qui utilisait environ 1 Go de RAM et qui fonctionnait sur une machine dédiée ne valait pas la peine d’être réparée. J’ai alors écrit un commentaire disant qu’elle avait fui, ce qu’il fallait changer pour réparer et pourquoi cela n’en valait pas la peine à l’époque.

5
Louis Gerbarg

J'imagine que c'est bien si vous écrivez un programme destiné à perdre de la mémoire (c'est-à-dire pour tester l'impact des pertes de mémoire sur les performances du système).

3
Beep beep

Je suis surpris de voir autant de définitions incorrectes de ce qu'est une fuite de mémoire. Sans définition concrète, une discussion sur le point de savoir si c'est une mauvaise chose ou non ira nulle part.

Comme certains commentateurs l'ont souligné à juste titre, une fuite de mémoire ne se produit que lorsque la mémoire allouée par un processus est hors de portée dans la mesure où le processus n'est plus en mesure de le référencer ou de le supprimer.

Un processus qui récupère de plus en plus de mémoire ne coule pas nécessairement. Tant qu'il est capable de référencer et de désallouer cette mémoire, il reste alors sous le contrôle explicite du processus et n'a pas fui. Le processus peut être mal conçu, en particulier dans le contexte d’un système où la mémoire est limitée, mais ce n’est pas la même chose qu’une fuite. Inversement, perdre la portée d'un tampon de 32 octets, par exemple, reste une fuite, même si la quantité de mémoire perdue est petite. Si vous pensez que cela est insignifiant, attendez que quelqu'un appelle un algorithme et l'appelle 10 000 fois.

Je ne vois aucune raison d'autoriser des fuites dans votre propre code, même minime. Les langages de programmation modernes tels que C et C++ déploient beaucoup d'efforts pour aider les programmeurs à prévenir de telles fuites et il est rarement un argument valable pour ne pas adopter de bonnes techniques de programmation - en particulier lorsqu'elles sont associées à des installations de langage spécifiques - pour éviter les fuites.

En ce qui concerne le code existant ou tiers, lorsque votre contrôle de la qualité ou votre capacité à apporter des modifications peut être très limité, en fonction de la gravité de la fuite, vous pouvez être contraint d'accepter ou de prendre des mesures d'atténuation, telles que le redémarrage régulier du processus l'effet de la fuite.

Il peut ne pas être possible de changer ou de remplacer le code existant (qui fuit) et par conséquent, vous pourriez être tenu de l'accepter. Cependant, ce n'est pas la même chose que déclarer que tout va bien.

3
Component 10

Même si vous êtes certain que votre fuite de mémoire «connue» ne causera pas de dégâts, ne le faites pas. Au mieux, cela vous permettra de faire une erreur similaire et probablement plus critique à un moment et à un endroit différents.

Pour moi, poser cette question revient à me demander "Puis-je éteindre le feu rouge à 3 heures du matin lorsque personne n'est là?". Bien sûr, cela ne posera pas de problème à ce moment-là, mais cela vous donnera un levier pour faire de même en heure de pointe!

3
Ather

Non, vous ne devriez pas avoir de fuites que le système d'exploitation va nettoyer pour vous. La raison (non mentionnée dans les réponses ci-dessus pour autant que je puisse vérifier) ​​est que vous ne savez jamais quand votre main () sera réutilisée comme fonction/module dans un autre programme . Si votre main () devient une fonction fréquemment appelée dans le logiciel d'une autre personne, ce logiciel aura une fuite de mémoire qui consommera de la mémoire au fil du temps.

KIV

2

En règle générale, si vous croyez que vous ne pouvez pas éviter les fuites de mémoire, vous devez réfléchir plus avant à la propriété d'un objet.

Mais à votre question, ma réponse est en bref En code de production, oui. Pendant le développement, non . Cela peut sembler arriéré, mais voici mon raisonnement:

Dans la situation que vous décrivez, où la mémoire est conservée jusqu'à la fin du programme, il est parfaitement correct de ne pas la libérer. Une fois votre processus terminé, le système d'exploitation nettoiera quand même. En fait, cela pourrait améliorer l'expérience de l'utilisateur: dans un jeu sur lequel j'ai travaillé, les programmeurs ont pensé qu'il serait plus propre de libérer toute la mémoire avant de quitter, ce qui entraînerait l'arrêt du programme jusqu'à une demi-minute! Un changement rapide qui vient d'appeler exit () à la place fait disparaître immédiatement le processus et ramène l'utilisateur sur le bureau où il le souhaite.

Cependant, vous avez raison en ce qui concerne les outils de débogage: ils vont faire l'affaire, et tous les faux positifs risquent de compliquer la recherche de vos fuites de mémoire réelles. Et pour cette raison, écrivez toujours un code de débogage qui libère de la mémoire et désactivez-le lors de l'expédition.

2
Enno

Ce n'est vraiment pas une fuite si c'est intentionnel et ce n'est pas un problème, sauf si c'est une quantité importante de mémoire, ou pourrait devenir une quantité importante de mémoire. Il est assez courant de ne pas nettoyer les allocations globales pendant la durée de vie d'un programme. Si la fuite se produit sur un serveur ou une application de longue durée, croît avec le temps, c'est un problème.

2
Sanjaya R

Je pense que vous avez répondu à votre propre question. Le plus gros inconvénient est la façon dont ils interfèrent avec les outils de détection de fuite de mémoire, mais je pense que cet inconvénient est un énorme inconvénient pour certains types d’applications.

Je travaille avec des applications de serveur héritées qui sont supposées être solides, mais elles ont des fuites et les globaux entravent les outils de détection de mémoire. C'est un gros problème. 

Dans le livre "Collapse" de Jared Diamond, l'auteur s'interroge sur ce qu'il pensait d'avoir abattu le dernier arbre de l'île de Pâques, l'arbre dont il aurait eu besoin pour construire un canot afin de quitter l'île. Je m'interroge sur le jour, il y a de nombreuses années, lorsque ce premier global a été ajouté à notre base de code. C’est le jour où il aurait dû être attrapé.

2
Corey Trager

Je vois le même problème que toutes les questions de scénario telles que celles-ci: que se passe-t-il lorsque le programme change, et tout à coup cette petite fuite de mémoire est appelée dix millions de fois et que la fin de votre programme se situe à un autre endroit, donc c'est important? S'il se trouve dans une bibliothèque, enregistrez un bogue avec les responsables de la bibliothèque, ne créez pas de fuite dans votre propre code.

2
tloach

Je réponds non.

En théorie, le système d'exploitation va nettoyer après vous si vous laissez un désordre (maintenant c'est juste grossier, mais puisque les ordinateurs n'ont pas de sentiments, cela pourrait être acceptable). Mais vous ne pouvez pas anticiper toutes les situations possibles pouvant survenir lors de l'exécution de votre programme. Par conséquent (à moins que vous ne puissiez effectuer une preuve formelle de certains comportements), la création de fuites de mémoire est tout simplement irresponsable et bâclée d'un point de vue professionnel.

Si un composant tiers perd de la mémoire, il s'agit d'un argument très puissant contre son utilisation, non seulement en raison de son effet imminent, mais également du fait que cela montre que les programmeurs travaillent de manière négligente et que cela pourrait également avoir une incidence sur d'autres métriques. Maintenant, lorsqu’on examine les systèmes existants, c’est difficile (considérons les composants de navigation Web: à ma connaissance, ils tous fuient la mémoire), mais cela devrait être la norme.

2
Konrad Rudolph

Je ne vois qu'un inconvénient pratique, à savoir que ces fuites bénignes apparaîtront comme des faux positifs avec les outils de détection des fuites de mémoire.

Si j'ai bien compris, vous ne libérez pas explicitement la mémoire (qui peut être libérée car vous avez toujours un pointeur) et vous vous fiez à OS pour le libérer lors de la fin du processus. Bien que cela puisse sembler correct pour un programme simple, envisagez le cas où votre code est déplacé dans une bibliothèque et devient partie intégrante d'un processus de démon résident exécuté 24/7. Dites que ce démon génère un thread à chaque fois qu'il doit faire quelque chose d'utile en utilisant votre code et qu'il génère des milliers de threads toutes les heures. Dans ce cas, vous aurez réel fuite de mémoire.

Malheureusement, cette situation n’est pas improbable dans la vie réelle et des techniques de gestion de la mémoire cohérentes peuvent vous faciliter la vie.

2
Dmitry Krivenok

Je suis d'accord avec vfilby - ça dépend. Sous Windows, nous traitons les fuites de mémoire comme des bogues relativement graves. Mais cela dépend beaucoup du composant. 

Par exemple, les fuites de mémoire ne sont pas très graves pour les composants qui fonctionnent rarement et pour une durée limitée. Ces composants s'exécutent, effectuent leur travail, puis se terminent. Quand ils sortent, toute leur mémoire est libérée implicitement. 

Cependant, les fuites de mémoire dans les services ou d’autres composants de longue durée (comme le shell) sont très graves. La raison en est que ces insectes "volent" la mémoire au fil du temps. La seule façon de le récupérer consiste à redémarrer les composants. La plupart des gens ne savent pas comment redémarrer un service ou le shell. Par conséquent, si les performances de leur système en pâtissent, ils doivent simplement redémarrer.

Donc, si vous avez une fuite - évaluez son impact de deux manières

  1. Pour votre logiciel et l'expérience de vos utilisateurs.
  2. Pour le système (et l'utilisateur) en termes de frugalité avec les ressources système.
  3. Impact du correctif sur la maintenance et la fiabilité.
  4. Probabilité de provoquer une régression ailleurs. 

Foredecker

2
Foredecker

Historiquement, cela importait sur certains systèmes d'exploitation dans certains cas Edge. Ces cas Edge pourraient exister à l'avenir.

Voici un exemple, sur SunOS à l’ère Sun 3, il existait un problème si un processus utilisait exec (ou plus généralement fork et ensuite exec), le nouveau processus ultérieur héritant de la même empreinte mémoire que le parent et ne pouvant être réduit . Si un processus parent allouait 1/2 Go de mémoire et ne la libérait pas avant d'appeler exec, le processus enfant commencerait à utiliser le même 1/2 Go (même s'il n'était pas alloué). SunTools (leur système de fenêtrage par défaut), qui était un bourreau de mémoire, a mieux présenté ce comportement. Chaque application créée a été créée à l’aide de fork/exec et de l’empreinte SunTools héritée, remplissant rapidement l’espace de permutation.

2
plinth

Cela a déjà été discuté ad nauseam . En fin de compte, une fuite de mémoire est un bogue et doit être corrigée. Si une bibliothèque tierce perd de la mémoire, on peut se demander ce qui ne va pas, non? Si vous construisiez une voiture, utiliseriez-vous un moteur qui laisse parfois échapper de l'huile? Après tout, quelqu'un d'autre a fabriqué le moteur, alors ce n'est pas de votre faute et vous ne pouvez pas le réparer, n'est-ce pas?

2
Dima

Généralement, une fuite de mémoire dans une application autonome n'est pas fatale, car elle est nettoyée à la fin du programme.

Que faites-vous pour les programmes serveur conçus pour ne pas se fermer?

Si vous êtes le genre de programmeur qui ne conçoit pas et n'implémente pas de code dans lequel les ressources sont allouées et publiées correctement, je ne veux rien avoir à faire avec vous ou votre code. Si vous ne vous souciez pas de nettoyer votre mémoire perdue, qu'en est-il de vos verrous? Les laissez-vous traîner là aussi? Laissez-vous de petites quantités de fichiers temporaires dans différents répertoires?

Vous perdez cette mémoire et laissez le programme le nettoyer? Non, absolument pas. C'est une mauvaise habitude, qui conduit à des bugs, des bugs et plus de bugs.

Nettoyez après vous-même. Yo maman ne travaille plus ici.

2
EvilTeach

J'ai pris une classe au lycée sur C et le professeur a dit de toujours s'assurer de libérer quand tu malloc. 

Mais quand j'ai pris un autre cours, le professeur a dit qu'il était acceptable de ne pas être libre pour les petits programmes qui ne durent qu'une seconde. Donc, je suppose que cela ne nuit pas à votre programme, mais c’est une bonne pratique de libérer du code fort et sain. 

1
azn_person

Il semble que votre définition de "fuite de mémoire" soit "une mémoire que je ne nettoie pas moi-même". Tous les systèmes d'exploitation modernes le libéreront à la sortie du programme. Cependant, comme il s’agit d’une question C++, vous pouvez simplement envelopper la mémoire en question dans un std::auto_ptr approprié qui appellera delete quand elle sortira du domaine.

1
Max Lybbert

Lorsqu'une application est fermée, on peut affirmer qu'il est préférable de ne pas libérer de mémoire.

En théorie, le système d'exploitation devrait libérer les ressources utilisées par l'application, mais certaines ressources font toujours exception à cette règle. Alors méfiez-vous.

Le bien avec juste quitter l'application:

  1. Le système d'exploitation obtient un morceau à libérer au lieu de beaucoup de petits morceaux. Cela signifie que l'arrêt est beaucoup plus rapide. Surtout sous Windows avec sa gestion de mémoire lente.

Le mauvais avec juste sortir est en fait deux points:

  1. Il est facile d'oublier de libérer des ressources que le système d'exploitation ne surveille pas ou que le système d'exploitation pourrait attendre un peu avant de les libérer. Un exemple est TCP sockets. 
  2. Le logiciel de suivi de la mémoire signalera tout pas libéré à la sortie en tant que fuite.

Pour cette raison, vous souhaiterez peut-être deux modes d’arrêt, l’un rapide et sale pour les utilisateurs finaux et l’autre lent et complet pour les développeurs. Assurez-vous juste de tester les deux :)

1
Jørn Jensen

Je suis tout à fait d’accord avec JohnMcG et je tiens simplement à ajouter que j’ai moi-même eu du mal à découvrir des fuites de mémoire réelles et potentiellement sérieuses à temps, simplement parce qu’il a été accepté d’avoir les bénignes. Lorsque ceux-ci sont devenus si nombreux au fil du temps, il devient de plus en plus difficile de détecter les problèmes graves dans l'inondation des effets bénins.

Donc, au moins pour vos collègues programmeurs (et aussi pour vous-même à l'avenir), essayez de les éléminer dès que possible.

1

Oui, une fuite de mémoire peut être le moindre de deux maux. Bien que l'exactitude soit importante, les performances ou la stabilité du système peuvent être affectées par la libération totale de la mémoire. De plus, les risques et le temps passé à libérer de la mémoire et à détruire des objets peuvent être moins souhaitables que de simplement quitter un processus.

En général, il n'est généralement pas acceptable de laisser de la mémoire. Il est difficile de comprendre toutes les portées dans lesquelles votre code sera exécuté et, dans certains cas, la fuite peut devenir catastrophique.

Que faire si vous allouez de la mémoire et que vous l’utilisez jusqu’à la toute dernière ligne de code de votre application (par exemple, le destructeur d’un objet global)?

Dans ce cas, votre code peut être porté dans un projet plus grand. Cela peut signifier que la durée de vie de votre objet est trop longue (elle dure pour tout le programme, pas seulement le cas où cela est nécessaire), ou que si le global est créé et détruit, il fuira.

est-il acceptable de faire confiance au système d'exploitation pour libérer votre mémoire lorsque l'application se termine?

Lorsqu'un programme de courte durée crée de grandes collections C++ (par exemple, std::map), il existe au moins 2 allocations par objet. Itérer à travers cette collection pour détruire les objets prend du temps réel pour le processeur, et laisser l'objet fuir et être rangé par le système d'exploitation présente des avantages en termes de performances. Le compteur indique que certaines ressources ne sont pas nettoyées par le système d’exploitation (par exemple, la mémoire partagée) et que la destruction de tous les objets de votre code entraîne le risque que certaines ressources restent sur ces ressources non libérées.

Et si une bibliothèque tierce vous forçait de force?

Tout d'abord, je voudrais soulever un bogue pour une fonction close qui libère les ressources. La question de savoir si cela est acceptable dépend du point de savoir si les avantages offerts par la bibliothèque (coût, performance, fiabilité) sont meilleurs que de le faire avec une autre bibliothèque ou de l'écrire vous-même.

En général, à moins que la bibliothèque ne soit réinitialisée, je ne serais probablement pas concerné.

moments acceptables pour avoir une mémoire de fuite signalée.

  1. Un service à l'arrêt. Ici, il y a un compromis entre performance temporelle et exactitude.
  2. Un objet cassé qui ne peut pas être détruit. J'ai été en mesure de détecter un objet défaillant (par exemple en raison d'une exception interceptée) et lorsque j'essaie de détruire l'objet, le résultat est un blocage.
  3. Vérificateur de mémoire mal signalé.

Un service à l'arrêt

Si le système d'exploitation est sur le point d'être désactivé, toutes les ressources seront rangées. L'avantage de ne pas effectuer un arrêt normal du processus est que l'utilisateur obtient des performances plus vives lorsqu'il est désactivé.

Un objet cassé

Dans mon passé, nous avons trouvé un objet (et soulevé un défaut pour cette équipe), que s'ils se brisaient à certains moments, ils étaient cassés, que toutes les fonctions suivantes de cet objet entraîneraient un blocage.

Bien qu’il soit peu pratique d’ignorer la fuite de mémoire, il était plus productif d’arrêter notre processus, en laissant filtrer l’objet et sa mémoire, que de provoquer un blocage.

déclaration erronée du vérificateur de fuite

Certains des vérificateurs de fuite fonctionnent en instrumentant des objets et se comportent de la même manière que les globals. Ils peuvent parfois oublier qu'un autre objet global possède un destructeur valide, appelé après la fin, qui libérerait la mémoire.

1
mksteve

Cela dépend vraiment de l'utilisation de l'objet qui crée la fuite de mémoire . Si vous créez l'objet plusieurs fois dans la vie de l'application qui l'utilise, il est alors mauvais de l'utiliser de cette façon. Parce qu’il y aura beaucoup de fuites de mémoire ... Par contre, si nous n’avons qu’une seule instance d’objet sans utiliser la mémoire et ne fuir que de manière réduite, ce n’est pas un problème.

La fuite de mémoire pose un problème lorsque la fuite augmente lorsque l'application est en cours d'exécution.

1
Vinay

Non, ils ne vont pas bien, mais j'ai implémenté quelques allocateurs, dumpers mémoire et détecteurs de fuites et j'ai constaté que, d'une manière pragmatique, il est commode de permettre à une telle allocation de marquer "Pas une fuite aussi loin." comme le rapport de fuite est concerné " ... 

Cela contribue à rendre le rapport de fuite plus utile ... et non encombré "d'allocation dynamique à portée statique non libérée par la sortie du programme"

0
reechard

Pensez au cas où l'application serait utilisée ultérieurement par une autre, avec la possibilité d'ouvrir plusieurs d'entre elles dans des fenêtres séparées ou les unes après les autres pour faire quelque chose. S'il ne s'agit pas d'un processus, mais en tant que bibliothèque, le programme appelant perd de la mémoire parce que vous pensiez ignorer le nettoyage de la mémoire.

Utilisez une sorte de pointeur intelligent qui le fait automatiquement pour vous (par exemple scoped_ptr de Boost libs)

0
Marius K

Couper les cheveux en quatre: si votre application fonctionne sous UNIX et peut devenir un zombie? Dans ce cas, la mémoire n'est pas récupérée par le système d'exploitation. Donc, je dis que vous devriez vraiment dé-allouer la mémoire avant la fin du programme.

0
Eric M

Quelques bonnes réponses ici. Pour ajouter une autre perspective à cette question, je traiterai le cas où une fuite de mémoire est non seulement acceptable, mais souhaitable : dans un environnement de pilotes Windows, le développeur fournit un ensemble de rappels exécutés par le système d'exploitation. chaque fois que nécessaire. L'un des rappels est un rappel d'arrêt, qui s'exécute avant que le système ne soit arrêté/redémarré. Contrairement aux situations standard, non seulement la libération de la mémoire n’est pas nécessaire (le système va être désactivé dans un instant), mais elle est même déconseillée - pour que l’arrêt soit le plus rapide possible et pour éviter la surcharge de la gestion de la mémoire.

0

Je crois que vous pouvez utiliser un programme qui ne durera que quelques secondes, puis cessera de fonctionner, et ce uniquement pour un usage personnel. Toute fuite de mémoire sera nettoyée dès la fin du programme. 

Le problème survient lorsque vous avez un programme qui fonctionne longtemps et que les utilisateurs en dépendent. En outre, il est de mauvaise habitude de laisser des fuites de mémoire dans votre programme, spécialement pour le travail, s’ils peuvent transformer ce code en quelque chose d’autre. 

Dans l'ensemble, il est préférable de supprimer les fuites de mémoire. 

0
PJT

Un seul cas: le programme va se lancer à cause d’une erreur irrécupérable.

0
Steve Lacey

Il y a quelque temps, j'aurais dit oui, il était parfois acceptable de laisser des fuites de mémoire dans votre programme (le prototypage est toujours en cours), mais après avoir fait 5 ou 6 fois l'expérience que le suivi de la moindre fuite révélait de très graves erreurs fonctionnelles. Laisser une fuite dans un programme se produit lorsque le cycle de vie d'une entité de données n'est pas vraiment connu, ce qui témoigne d'un manque d'analyse insignifiant. En conclusion, c’est toujours une bonne idée de savoir ce qui se passe dans un programme.

0
Patrick Schlüter

La meilleure pratique consiste à toujours libérer ce que vous allouez, en particulier si vous écrivez quelque chose qui est conçu pour être exécuté pendant toute la durée de disponibilité d'un système, même lors du nettoyage avant la sortie.

C’est une règle très simple. Programmer avec l’intention de ne pas avoir de fuite permet de repérer facilement les nouvelles fuites. Vendriez-vous à quelqu'un une voiture que vous avez fabriquée en sachant que de l'essence pulvérisée sur le sol était éteinte chaque fois? :)

Quelques appels si () free () dans une fonction de nettoyage sont peu coûteux, pourquoi ne pas les utiliser? 

0
Tim Post

Il est parfaitement acceptable d’omettre de libérer de la mémoire sur la dernière ligne du programme car le libérer n’aurait aucun effet, le programme n’ayant plus jamais besoin de mémoire.

0
henle

La règle est simple: si vous avez fini d'utiliser de la mémoire, nettoyez-la ..__ et parfois même si nous avons besoin de quelques instances plus tard, mais nous remarquons que nous utilisons énormément de mémoire afin qu'elle puisse avoir une incidence sur la préformance due au basculement sur disque, nous pouvons stocker des données aux fichiers sur le disque et après les recharger, cette technique optimise parfois beaucoup votre programme.

0
mike

Si vous l'utilisez jusqu'à la fin de votre main(), il ne s'agit tout simplement pas d'une fuite (dans le cas d'un système de mémoire protégé, bien sûr!).

En fait, libérer des objets à l’arrêt du processus est la chose absolue que vous pouvez faire pire ... le système d’exploitation doit revenir en arrière dans chaque page que vous avez créée. Fermez les descripteurs de fichiers, les connexions à la base de données, bien sûr, mais libérer de la mémoire n’est que stupide.

0
Simon Buchan

Tant que votre utilisation de la mémoire n'augmente pas avec le temps, cela dépend. Si vous effectuez beaucoup de synchronisations complexes dans le logiciel serveur, par exemple, en démarrant les threads d'arrière-plan qui bloquent les appels système, il peut s'avérer trop complexe d'effectuer un arrêt complet. Dans cette situation, les alternatives peuvent être:

  1. Votre bibliothèque qui ne nettoie pas sa mémoire jusqu'à la fin du processus.
  2. Vous écrivez 500 lignes de code supplémentaires et ajoutez un autre mutex et une variable de condition à votre classe afin qu'elle puisse se fermer proprement à partir de vos tests - mais ce code n'est jamais utilisé en production, où le serveur se termine uniquement par un blocage.
0
Jonathan

Si votre code présente des fuites de mémoire, même connues, des fuites "acceptables", vous aurez un temps ennuyeux à utiliser des outils de fuite de mémoire pour rechercher vos "vraies" fuites. Tout comme le fait de laisser des avertissements "acceptables" du compilateur rend plus difficile la recherche de nouveaux "vrais" avertissements.

0
Chris Peterson