Dans de nombreux livres et tutoriels, j'ai entendu parler de la gestion de la mémoire et de certaines pratiques mystérieuses et terribles si je ne libérais pas la mémoire après l'avoir utilisée.
Je ne peux pas parler pour d’autres systèmes (même s’il est raisonnable de supposer qu’ils adoptent une pratique similaire), mais au moins sous Windows, le noyau est en principe garanti de nettoyer la plupart des ressources (à l’exception d’un petit nombre impair) utilisées par un programme après la fin du programme. Qui comprend la mémoire de tas, entre autres choses.
Je comprends pourquoi vous voudriez fermer un fichier après l’avoir utilisé pour le mettre à la disposition de l’utilisateur ou pourquoi vous voudriez déconnecter un socket connecté à un serveur afin de gagner de la bande passante, mais cela semble ridicule. avoir à gérer tout votre mémoire utilisée par votre programme.
Maintenant, je suis d' accord que cette question est large depuis la façon dont vous devez gérer votre mémoire est basée sur la quantité de mémoire dont vous avez besoin et quand vous en avez besoin, je vais donc limiter la portée de cette question à ceci: Si je dois utiliser un morceau de mémoire tout au long de la vie de mon programme, est-il vraiment nécessaire de le libérer juste avant la fin du programme?
Edit: La question suggérée comme un doublon était spécifique à la famille de systèmes d'exploitation Unix. Sa première réponse spécifiait même un outil spécifique à Linux (par exemple, Valgrind). Cette question vise à couvrir la plupart des systèmes d'exploitation "normaux" non intégrés et explique pourquoi il est recommandé ou non de libérer de la mémoire si nécessaire tout au long de la vie d'un programme.
la source
Réponses:
Ce n'est pas obligatoire, mais cela peut avoir des avantages (ainsi que des inconvénients).
Si le programme alloue de la mémoire une fois pendant son temps d'exécution et ne la libérait jamais avant la fin du processus, il peut être judicieux de ne pas libérer la mémoire manuellement et de ne pas s'appuyer sur le système d'exploitation. Sur tous les systèmes d’exploitation modernes que je connais, c’est sûr, à la fin du processus, toute la mémoire allouée est restituée de manière fiable au système.
Dans certains cas, le fait de ne pas nettoyer explicitement la mémoire allouée peut même être nettement plus rapide que de procéder au nettoyage.
Cependant, en libérant explicitement toute la mémoire en fin d’exécution,
La durée de vie des programmes peut changer. Aujourd'hui, votre programme est peut-être un petit utilitaire de ligne de commande, avec une durée de vie moyenne de moins de 10 minutes, et alloue de la mémoire par portions de quelques kb toutes les 10 secondes - vous n'avez donc pas besoin de libérer la mémoire allouée avant la fin du programme. Plus tard, le programme est modifié et obtient une utilisation étendue dans le cadre d’un processus serveur d’une durée de vie de plusieurs semaines. Ne libérez donc pas de mémoire inutilisée entre ces options. Sinon, votre programme consommera progressivement toute la mémoire disponible du serveur. . Cela signifie que vous devrez revoir l'ensemble du programme et ajouter du code de désaffectation par la suite. Si vous avez de la chance, la tâche est facile, sinon, il se peut que vous ayez de grandes chances de rater une place. Et quand vous serez dans cette situation, vous voudrez avoir ajouté le "gratuit"
Plus généralement, l'écriture d'allocation et de code de désallocation associé est toujours considérée comme une "bonne habitude" par de nombreux programmeurs: en faisant cela toujours, vous réduisez la probabilité d'oubli du code de désallocation dans des situations où la mémoire doit être libérée.
la source
main
.La libération de mémoire à la fin de l'exécution d'un programme n'est qu'une perte de temps CPU. C'est comme ranger une maison avant de la sortir de son orbite.
Cependant, un programme de courte durée peut parfois devenir un programme beaucoup plus long. Libérer des choses devient alors nécessaire. Si cela n'a pas été envisagé au moins dans une certaine mesure, cela peut impliquer un remaniement important.
Une solution intelligente à cela est "talloc" qui vous permet de faire une charge d'allocations de mémoire et de les jeter ensuite en un seul appel.
la source
free
est généralement beaucoup plus rapide quemalloc
.Vous pouvez utiliser une langue avec garbage collection (telle que Scheme, Ocaml, Haskell, Common Lisp et même Java, Scala, Clojure).
(Dans la plupart des langages GC-ed, il n’existe aucun moyen de libérer explicitement et manuellement la mémoire! Parfois, certaines valeurs peuvent être finalisées , par exemple, le GC et le système d’exécution feraient fermer une valeur de descripteur de fichier lorsque cette valeur est inaccessible; bien sûr, non fiable, et vous devriez plutôt fermer explicitement vos descripteurs de fichiers, car la finalisation n'est jamais garantie)
Vous pouvez également, pour votre programme codé en C (ou même C ++), utiliser le récupérateur de déchets conservateur de Boehm . Vous remplaceriez alors tous vos
malloc
objetsGC_malloc
et ne vousfree
soucieriez plus d'aucun pointeur. Bien sûr, vous devez comprendre les avantages et les inconvénients de l’utilisation du GC de Boehm. Lisez aussi le manuel du GC .La gestion de la mémoire est une propriété globale d'un programme. D'une certaine manière, il (et la vivacité de certaines données) est non-compositionnel et non modulaire, puisqu'il s'agit de la propriété d'un programme entier.
Enfin, comme d'autres l'ont souligné, la
free
création de zones de mémoire C allouées au segment de disque est une bonne pratique. Pour un programme jouet n'allouant pas beaucoup de mémoire, vous pouvez même décider de ne pas utiliser defree
mémoire du tout (car une fois le processus terminé, ses ressources, y compris son espace d'adressage virtuel , seraient libérées par le système d'exploitation).Non, vous n'avez pas besoin de faire ça. Et de nombreux programmes du monde réel ne se soucient pas de libérer de la mémoire nécessaire sur toute la durée de vie (en particulier, le compilateur GCC ne libère pas une partie de sa mémoire). Cependant, lorsque vous faites cela (par exemple, vous ne vous souciez pas de
free
certaines données allouées dynamiquement en C ), vous feriez mieux de commenter ce fait pour faciliter le travail des futurs programmeurs sur le même projet. J'ai tendance à recommander que la quantité de mémoire non libérée reste limitée et qu'elle soit relativement petite par rapport à la mémoire totale utilisée.Notez que
free
souvent le système ne libère pas de mémoire dans le système d’exploitation (par exemple, en appelant munmap (2) sur les systèmes POSIX), mais marque généralement une zone mémoire comme étant réutilisable par la suitemalloc
. En particulier, l'espace d'adressage virtuel (comme vu/proc/self/maps
dans Linux, voir proc (5) ...) pourrait ne pas être réduit aprèsfree
(d'où des utilitaires commeps
outop
qui signalent la même quantité de mémoire utilisée par votre processus).la source
Ce n'est pas nécessaire , car vous ne manquerez pas d'exécuter correctement votre programme si vous échouez. Cependant, il y a des raisons pour lesquelles vous pouvez choisir de le faire, si vous en avez l'occasion.
L'un des cas les plus puissants que je rencontre (encore et encore) est que quelqu'un écrit un petit morceau de code de simulation qui s'exécute dans leur exécutable. Ils disent "nous aimerions que ce code soit intégré à la simulation". Je leur demande ensuite comment ils envisagent de se réinitialiser entre les courses de monte-carlo et ils me regardent sans rien comprendre. "Que voulez-vous dire par réinitialisation? Vous venez d'exécuter le programme avec de nouveaux paramètres?"
Parfois, un nettoyage en profondeur facilite grandement l’utilisation de votre logiciel. Dans le cas de nombreux exemples, vous présumez que vous n’avez jamais besoin de nettoyer quelque chose et vous faites des hypothèses sur la façon dont vous pouvez gérer les données et leur durée de vie autour de ces hypothèses. Lorsque vous passez dans un nouvel environnement où ces présomptions ne sont pas valides, des algorithmes entiers risquent de ne plus fonctionner.
Pour illustrer à quel point des choses étranges peuvent devenir étranges, examinez la manière dont les langages gérés gèrent la finalisation à la fin des processus ou la manière dont C # gère l’arrêt par programmation des domaines d’application. Ils se lient en nœuds parce qu'il y a des hypothèses qui tombent à l'eau dans ces cas extrêmes.
la source
Ignorer les langues où vous ne libérez pas de mémoire manuellement de toute façon ...
Ce que vous considérez comme "un programme" actuellement pourrait à un moment donné devenir simplement une fonction ou une méthode faisant partie d’un programme plus vaste. Et puis cette fonction peut être appelée plusieurs fois. Et puis la mémoire que vous devriez avoir "libérée manuellement" sera une fuite de mémoire. C'est bien sûr un jugement.
la source
Parce qu'il est très probable que vous souhaitiez modifier votre programme dans un certain temps, peut-être l'intégrer à autre chose, exécuter plusieurs instances en séquence ou en parallèle. Il deviendra alors nécessaire de libérer manuellement cette mémoire - mais vous ne vous souviendrez plus des circonstances et cela vous coûtera beaucoup plus de temps pour ré-comprendre votre programme.
Faites les choses pendant que votre compréhension à leur sujet est encore fraîche.
C'est un petit investissement qui peut générer de gros rendements dans le futur.
la source
Non, ce n'est pas nécessaire, mais c'est une bonne idée.
Vous dites que vous pensez que "des choses mystérieuses et terribles se produiraient si je ne libérais pas la mémoire une fois l'utilisation terminée".
Sur le plan technique, la seule conséquence est que votre programme continuera à consommer plus de mémoire jusqu'à ce qu'une limite stricte soit atteinte (par exemple, votre espace d'adressage virtuel est épuisé) ou que les performances deviennent inacceptables. Si le programme est sur le point de se terminer, rien de tout cela n'a d'importance, car le processus cesse effectivement d'exister. Les "choses mystérieuses et terribles" concernent uniquement l'état mental du développeur. Trouver la source d'une fuite de mémoire peut être un cauchemar absolu (c'est un euphémisme) et il faut beaucoup de talent et de discipline pour écrire du code sans fuites. L'approche recommandée pour développer cette compétence et cette discipline consiste à toujours libérer de la mémoire dès qu'elle n'est plus nécessaire, même si le programme est sur le point de se terminer.
Bien entendu, cela présente l’avantage supplémentaire que votre code peut être réutilisé et adapté plus facilement, comme d’autres l’ont dit.
Cependant , il existe au moins un cas où il est préférable de ne pas libérer de mémoire juste avant la fin du programme.
Prenons le cas où vous avez effectué des millions de petites allocations et qui ont généralement été permutées sur disque. Lorsque vous commencez à tout libérer, la plus grande partie de votre mémoire doit être reconvertie en RAM afin de pouvoir accéder aux informations de comptabilité et les supprimer immédiatement. Cela peut rendre le programme prendre quelques minutes juste pour sortir! Sans parler de mettre beaucoup de pression sur le disque et la mémoire physique pendant ce temps. Si la mémoire physique est insuffisante pour commencer (le programme est peut-être fermé car un autre programme consomme beaucoup de mémoire), il peut alors être nécessaire d’échanger plusieurs pages individuelles lorsque plusieurs objets doivent être libérés de la mémoire. même page, mais ne sont pas libérés consécutivement.
Si, au lieu de cela, le programme abandonne simplement, le système d'exploitation supprimera simplement toute la mémoire qui a été échangée sur le disque, ce qui est presque instantané car il ne nécessite aucun accès au disque.
Il est important de noter que dans un langage OO, l'appel du destructeur d'un objet forcera également la mémoire à être échangée; Si vous devez le faire, vous pouvez également libérer la mémoire.
la source
Les principales raisons de nettoyer manuellement sont les suivantes: vous êtes moins enclin à confondre une fuite de mémoire véritable avec un bloc de mémoire qui sera nettoyé à la sortie, vous rencontrez parfois des bogues dans l'allocateur uniquement lorsque vous parcourez l'intégralité de la structure de données. désallouer, et vous aurez beaucoup plus petit mal de tête si vous refactoring jamais de sorte que certains objets ne doivent se désallouée avant la fin du programme.
Les principales raisons de ne pas nettoyer manuellement sont les suivantes: performances, performances, performances et possibilité qu'un bogue free-two ou use-after-free apparaisse dans votre code de nettoyage inutile, ce qui peut se transformer en bogue d'arrêt ou en sécurité exploit.
Si vous vous souciez de la performance, vous voulez toujours définir votre profil pour savoir où vous perdez tout votre temps, au lieu de deviner. Un compromis possible consiste à envelopper votre code de nettoyage optionnel dans un bloc conditionnel, à le laisser lors du débogage pour bénéficier des avantages de l'écriture et du débogage du code, et ensuite, si et seulement si vous avez déterminé de manière empirique que c'est trop. overhead, dites au compilateur de le sauter dans votre exécutable final. Et un délai dans la clôture du programme est, presque par définition, rarement dans le chemin critique.
la source