Lorsque vous quittez une application C, la mémoire malloc-ed est-elle automatiquement libérée?

92

Disons que j'ai le code C suivant:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Lorsque je compile et exécute ce programme C, c'est-à-dire après avoir alloué un peu d'espace en mémoire, cette mémoire que j'ai allouée sera-t-elle toujours allouée (c'est-à-dire occupant essentiellement de l'espace) après avoir quitté l'application et le processus se termine?

Andreas Grech
la source
9
c'est "bon style" de nettoyer votre mémoire, non pas parce que vous pourriez fonctionner sur un système d'exploitation qui n'a pas de mémoire protégée (ce qui est la principale suggestion ci-dessous), mais parce que cela augmente la probabilité que vous trouviez des fuites de mémoire, et conserve votre code maigre et correct ...
Matt Joiner

Réponses:

111

Cela dépend du système d'exploitation. La majorité des systèmes d'exploitation modernes (et tous les principaux) libéreront de la mémoire non libérée par le programme à la fin.

S'en remettre à cela est une mauvaise pratique et il vaut mieux la libérer explicitement. Le problème n'est pas seulement que votre code semble mauvais. Vous pouvez décider d'intégrer votre petit programme dans un programme plus grand et plus long. Puis, un peu plus tard, vous devrez passer des heures à dépister les fuites de mémoire.
S'appuyer sur une fonctionnalité d'un système d'exploitation rend également le code moins portable.

Yacoby
la source
16
Une fois, j'ai rencontré win98 sur une plate-forme intégrée, et sur la base de cette expérience, je peux dire qu'il ne libère PAS de mémoire lorsque les programmes se ferment.
San Jacinto
8
@Ken C'était un exemple. En outre, il y a une ligne entre YAGNI et le codage bâclé. Ne pas libérer de ressources le traverse. Le principe YAGNI était également destiné à être appliqué aux fonctionnalités, et non au code qui permet au programme de fonctionner correctement. (Et ne pas libérer de mémoire est un bogue).
Yacoby
5
+1: la chose la plus importante à considérer est que la gestion de la mémoire est comme Yacoby le déclare assez correctement: "une caractéristique du système d'exploitation" . Sauf erreur de ma part, le langage de programmation ne définit pas ce qui se passe avant ou après l'exécution du programme.
D.Shawley
9
Libérer de la mémoire manuellement prend plus de temps, prend plus de code et introduit la possibilité de bogues (dites-moi que vous n'avez jamais vu de bogue dans le code de désallocation!). Il n'est pas «bâclé» d'omettre intentionnellement quelque chose qui est pire à tous points de vue pour votre cas d'utilisation particulier. À moins ou jusqu'à ce que vous vouliez l'exécuter sur un système ancien / minuscule qui ne peut pas libérer de pages après la fin du processus, ou l'intégrer dans un programme plus grand (YAGNI), cela ressemble à une perte nette pour moi. Je sais que ça fait mal à l'ego d'un programmeur de penser à ne pas le nettoyer soi-même, mais de quelle manière pratique est-ce vraiment mieux?
Ken
6
Quiconque propose des fuites de mémoire sur SO devrait être dépouillé de toute réputation et de tout badge
Ulterior
53

En général, les systèmes d'exploitation modernes à usage général nettoient après l'arrêt des processus . Cela est nécessaire parce que l'alternative est que le système perd des ressources au fil du temps et nécessite un redémarrage en raison de programmes mal écrits ou qui ont simplement des bogues rares qui fuient des ressources.

Le fait que votre programme libère de toute façon explicitement ses ressources peut être une bonne pratique pour diverses raisons, telles que:

  • Si vous avez des ressources supplémentaires qui ne sont pas nettoyées par le système d'exploitation à la sortie, comme des fichiers temporaires ou tout type de modification de l'état d'une ressource externe, vous aurez besoin de code pour gérer toutes ces choses à la sortie, et ce est souvent élégamment combiné avec la libération de mémoire.
  • Si votre programme commence à avoir une durée de vie plus longue, vous ne voudrez pas que le seul moyen de libérer de la mémoire soit de quitter. Par exemple, vous pouvez convertir votre programme en un serveur (démon) qui continue de fonctionner tout en traitant de nombreuses demandes pour des unités de travail individuelles, ou votre programme peut devenir une petite partie d'un programme plus vaste.

Cependant, voici une raison pour ne pas libérer de mémoire: un arrêt efficace . Par exemple, supposons que votre application contienne un grand cache en mémoire. Si, à sa sortie, il traverse toute la structure du cache et le libère une pièce à la fois, cela ne sert à rien et gaspille des ressources. En particulier, considérez le cas où les pages de mémoire contenant votre cache ont été échangées sur le disque par le système d'exploitation; en parcourant la structure et en la libérant, vous ramenez toutes ces pages en mémoire en une seule fois , gaspillant beaucoup de temps et d'énergie pour aucun avantage réel, et peut-être même provoquant l'échange d'autres programmes du système!

À titre d'exemple connexe, il existe des serveurs hautes performances qui fonctionnent en créant un processus pour chaque demande, puis en le faisant quitter une fois terminé; par ce moyen, ils n'ont même pas besoin de suivre l' allocation de mémoire , et ne font jamais du tout de libération ou de garbage collection, puisque tout disparaît simplement dans la mémoire libre du système d'exploitation à la fin du processus. (Le même genre de chose peut être fait dans un processus en utilisant un allocateur de mémoire personnalisé, mais nécessite une programmation très minutieuse; en faisant essentiellement sa propre notion de «processus légers» dans le processus du système d'exploitation.)

Kevin Reid
la source
11

Mes excuses pour avoir publié si longtemps après le dernier message sur ce fil.

Un point supplémentaire. Tous les programmes ne parviennent pas à des sorties élégantes. Les plantages et les ctrl-C, etc. entraîneront la fermeture d'un programme de manière incontrôlée. Si votre système d'exploitation ne libère pas votre tas, nettoie votre pile, supprime les variables statiques, etc., vous finirez par planter votre système à cause de fuites de mémoire ou pire.

En dehors de cela, les plantages / pannes dans Ubuntu, et je soupçonne que tous les autres systèmes d'exploitation modernes, ont des problèmes avec les ressources "gérées". Les sockets, fichiers, périphériques, etc. peuvent rester "ouverts" lorsqu'un programme se termine / plante. C'est également une bonne pratique pour fermer tout ce qui a une «poignée» ou un «descripteur» dans le cadre de votre nettoyage avant une sortie gracieuse.

Je développe actuellement un programme qui utilise fortement les sockets. Lorsque je suis coincé dans un blocage, je dois ctrl-c hors de celui-ci, bloquant ainsi mes prises. J'ai ajouté un std :: vector pour collecter une liste de toutes les sockets ouvertes et un gestionnaire de sigaction qui attrape sigint et sigterm. Le gestionnaire parcourt la liste et ferme les sockets. Je prévois de faire une routine de nettoyage similaire à utiliser avant les lancers qui mèneront à une interruption prématurée.

Quelqu'un souhaite-t-il commenter cette conception?

Wes Miller
la source
1
Je suis heureux que vous ayez dit cela, car j'avais un programme qui laissait des ressources de socket, et notre système Ubuntu avait besoin d'un redémarrage toutes les deux semaines ou la mémoire commençait à manquer, et il y a beaucoup de mémoire. Je ne suis pas sûr que les ressources système soient détruites si vous oubliez de les nettoyer.
octopusgrabbus
8
Stackoverflow n'est pas un forum; il n'y a rien de mal à répondre à une vieille question. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

Ce qui se passe ici ( dans un système d'exploitation moderne ), c'est que votre programme s'exécute dans son propre «processus». Il s'agit d'une entité de système d'exploitation dotée de son propre espace d'adressage, de descripteurs de fichiers, etc.malloc appels allouent la mémoire du « tas » ou des pages de mémoire non alloués qui sont affectés à votre processus.

Lorsque votre programme se termine, comme dans cet exemple, toutes les ressources affectées à votre processus sont simplement recyclées / détruites par le système d'exploitation. Dans le cas de la mémoire, toutes les pages mémoire qui vous sont attribuées sont simplement marquées comme «libres» et recyclées pour l'utilisation d'autres processus. Les pages sont un concept de niveau inférieur à celui de malloc - en conséquence, les spécificités de malloc / free sont toutes simplement emportées au fur et à mesure que tout est nettoyé.

C'est l'équivalent moral de, lorsque vous avez fini d'utiliser votre ordinateur portable et que vous voulez le donner à un ami, vous ne vous souciez pas de supprimer individuellement chaque fichier. Vous formatez simplement le disque dur.

Tout cela étant dit, comme le font remarquer tous les autres répondants, se fier à ceci n'est pas une bonne pratique:

  1. Vous devriez toujours programmer pour vous occuper des ressources, et en C cela signifie aussi la mémoire. Vous pourriez finir par incorporer votre code dans une bibliothèque, ou il pourrait finir par s'exécuter beaucoup plus longtemps que prévu.
  2. Certains systèmes d'exploitation (les plus anciens et peut-être certains intégrés modernes) peuvent ne pas maintenir ces limites de processus strictes et vos allocations peuvent affecter les espaces d'adressage des autres.
Ben Zotto
la source
4

Oui. Le système d'exploitation nettoie les ressources. Eh bien ... les anciennes versions de NetWare ne l'ont pas fait.

Edit: Comme l'a souligné San Jacinto, il existe certainement des systèmes (à part NetWare) qui ne font pas cela. Même dans les programmes jetables, j'essaie de prendre l'habitude de libérer toutes les ressources juste pour garder l'habitude.

Mark Wilkins
la source
3
Je ne suis pas contre, mais c'est un joli article sur les dangers pour la postérité. DOS est toujours utilisé sur de nombreuses plates-formes embarquées, et je doute sérieusement qu'il effectue le nettoyage de la mémoire à votre place. La généralisation radicale est fausse.
San Jacinto
@San Jacinto: C'est un bon point. C'est pourquoi j'ai fait la référence NetWare, mais cela pourrait probablement être utile. Je vais le modifier un peu.
Mark Wilkins
3
@San DOS n'est pas un système d'exploitation multitâche - lorsqu'un programme DOS (à l'exclusion des TSR) se termine, toute la mémoire est disponible pour le prochain programme à charger.
@Neil merci pour le rappel, mais je faisais référence à un programme de type TSR qui se lancerait lorsqu'un événement se produirait, comme c'est une utilisation courante pour les systèmes embarqués. Néanmoins, merci pour votre expertise et vos éclaircissements là où j'ai échoué :)
San Jacinto
2

Oui, le système d'exploitation libère toute la mémoire lorsque le processus se termine.

Draemon
la source
Je ne vois pas pourquoi cela a été rejeté. La mémoire malloc'ed sera libérée lorsque le processus meurt (la définition wikipedia de malloc le dit)
Arve
7
Wikipédia n'est pas le manuel de tous les systèmes d'exploitation existants. La plupart des systèmes d'exploitation modernes récupèrent la mémoire, mais pas tous (et en particulier tous les anciens). Ajoutez à cela, mallocne peut que promettre ce que C fera de la mémoire; de par sa conception, C ne garantit pas grand-chose concernant le comportement en dehors de C lui-même. Si l'application meurt de manière inattendue, toutes les promesses faites par la bibliothèque d'exécution sont nulles et non avenues, car elle n'est plus vivante pour les tenir.
cHao
2

Cela dépend, les systèmes d'exploitation le nettoieront généralement pour vous, mais si vous travaillez par exemple sur un logiciel intégré, il se peut qu'il ne soit pas publié.

Assurez-vous simplement de le libérer, cela peut vous faire gagner beaucoup de temps plus tard, lorsque vous voudrez peut-être l'intégrer à un grand projet.

Charles
la source
0

Cela dépend vraiment du système d'exploitation, mais pour tous les systèmes d'exploitation que vous rencontrerez jamais, l'allocation de mémoire disparaîtra lorsque le processus se terminera.


la source
0

Je pense que la libération directe est la meilleure. Un comportement non défini est la pire des choses, donc si vous avez accès pendant qu'il est encore défini dans votre processus, faites-le, il y a beaucoup de bonnes raisons que les gens ont données pour cela.

Quant à savoir où, ou si, j'ai trouvé cela dans W98, la vraie question était «quand» (je n'ai pas vu un article insistant sur cela). Un petit programme de modèle (pour l'entrée MIDI SysEx, utilisant divers espaces malloc'd) libérerait de la mémoire dans le bit WM_DESTROY du WndProc, mais quand je l'ai transplanté dans un programme plus grand, il s'est écrasé à la sortie. J'ai supposé que cela signifiait que j'essayais de libérer ce que le système d'exploitation avait déjà libéré lors d'un nettoyage plus important. Si je l'ai fait sur WM_CLOSE, puis appelé DestroyWindow (), tout a bien fonctionné, sortie propre instantanée.

Bien que ce ne soit pas exactement la même chose que les tampons MIDI, il existe une similitude en ce sens qu'il est préférable de garder le processus intact, de nettoyer complètement, puis de quitter. Avec des morceaux de mémoire modestes, c'est très rapide. J'ai trouvé que de nombreux petits tampons fonctionnaient plus rapidement en fonctionnement et en nettoyage que moins de grands tampons.

Des exceptions peuvent exister, comme quelqu'un l'a dit en évitant de retirer de gros morceaux de mémoire d'un fichier d'échange sur disque, mais même cela peut être minimisé en gardant des espaces alloués plus nombreux et plus petits.


la source