Forum Programmation.c free() or not free() ?

Posté par  (site web personnel) .
Étiquettes : aucune
0
22
fév.
2005
Bonjour,

on vient de me demander si il etait vraiment nécéssaire d'appeler free() en fin de programme, en argumentant sur le fait que l'OS devait faire le ménage.

J'avoue que pour ma part j'ai toujours placer un free pour chacun de mes malloc avant le return, et je n'ai jamais vraiment cherché a savoir si c'etait si utile que ca (dans ma tete les blocs non-désalloués restaient bloqués jusqu'au reboot de la machine..)

Donc j'aimerai une réponse sur la question, que je ne meurt pas bête :)

Merci.
  • # mon avis.

    Posté par  (site web personnel) . Évalué à 6.

    Ca ne coûte rien d'en mettre dans le doute.
    Mais de manière générale c'est une bonne pratique, puisqu'il responsabilise le programmeur qui devient conscient des ressources qu'il utilise, et s'il faut bien son taf il pensera même à libérer la mémoire avant la fin du programme, quand il ne s'en sert plus, évitant ainsi de se retrouver avec une appli qui grossit, qui grossit, et qui fini par saturer l'OS qui n'a plus rien à lui donner à bouffer.

    Donc de manière générale, les free c'est bien, mangez-en.
  • # Vraiment fin de programme

    Posté par  (site web personnel) . Évalué à 4.

    Tu fais un prog.
    A la ligne 50, il quitte. Donc tu ne t'embettes pas a liberer les ressource, l'OS va le faire juste apres.
    ton prog evolue.
    Entre la ligne 49 et 50, tu inseres une boucle de deux heure qui fait une deuxieme chose, et qui n'a pas besoin de la memoire allouée avant la ligne 49.
    Bilan : tu consommes de la memoire inutilement (celle allouée avant la ligne 49) pendant 2h.

    Resumé : ca ne sert pas maintenant de le faire, mais peut-etre qu'un jour ca servira, dans le dout met free()
    (bon, moi je fais delete() plutot, mais chacun son language ;-) )
    • [^] # Re: Vraiment fin de programme

      Posté par  (site web personnel) . Évalué à 2.

      autre cas pratique:

      dans le main, j ai une double boucle for qui appelle une sous routine.

      La sous routine fait elle meme une double boucle for apres avoir alloue plusieur des tableaux assez grands.

      La routine alloue environ 4 tableaux de dimention variables ( des tableaux de 1 a 3D de taille totalement dynamique ) ... un enfer a allouer, et c est pire a desalouer.

      Chaque for tourne environ 1000 tours. donc le main appelle 1000000 la sous routine. Laroutine alloue des tableaux de 1 a 10M. T imagine su je me gourre dans mes free ?

      un free de trop, mal place, soit je libere un truc pas encore alloue, soit je libere une ressource dont je pourrai avoir besoin plus loin. Si je ne fait pas de free du tout, mon main appel 1000000 fois la sous routine qui alloue minimum 1Mo a chaque appel ... si je libere pas du tout, il me faudrait 1To de RAM ...

      Donc meme si tout est libere apres execution, le truc risque de swapper plusieur jours ... dans la limite de la swap disponible ... et j ai deja 3Go de swap sur mes 350Go de disque.

      Avec des free bien places, le tout s execute a peu pres bien avec un pic de consommation a environ 150Mo de RAM.

      Enfin 150M, c est quand je manipule des images non compressees de 10Mo a la base. J ai en stoque une image de 80Mo ... elle j ai peur ... je crois que je vais jamais la traiter :)

      ( et je ne travailles qu en 8 bits per pixel).

      Mais je vais tester de virer les free, je pense que ca va swapper severe avant meme de finire la premiere etape du traitement :)
  • # ca depend...

    Posté par  . Évalué à 2.

    si tu veux que ton code soit portable facilement, fais le.
    sinon fais comme tu veux.
  • # Plop

    Posté par  (site web personnel) . Évalué à 4.

    Bon bah merci pour vos commentaires.
    Ca confirme donc mon attitude : mettre autant de free qu'il y a de malloc.

    question subsidiaire : ya t il un OS qui ne releaserait pas la mémoire en fin de programme ?
    • [^] # Re: Plop

      Posté par  . Évalué à 2.

      Windows 9x? On a justement posé la question dans un cours hier! C'est ce que le prof a répondu et ça ne me surprend pas!
    • [^] # Re: Plop

      Posté par  . Évalué à 2.

      Si un tel OS existait, alors n'importe quel utilisateur/processus non privilégié pourrait lancer un DOS tout simple, avec un simple while(1) malloc(1); .

      Donc, à mon avis, si tu trouves un OS de ce type, il ne mérite pas le titre d'OS.
      • [^] # Re: Plop

        Posté par  . Évalué à 1.

        ca existe. entre autre dans l'embarque.
        • [^] # Re: Plop

          Posté par  . Évalué à 2.

          Je m'attendais à ce qu'on me cite l'embarqué... Mais est-ce qu'on peut encore considérer qu'on est en présence d'un OS si n'importe quelle application peut mettre l'ensemble du système à mal?

          Pour moi, non. Et, dans ce cas-là, il faut soit considérer que toutes les applications font partie de l'OS, soit considérer qu'il n'y a pas d'OS du tout.
          • [^] # Re: Plop

            Posté par  . Évalué à 2.

            Mais est-ce qu'on peut encore considérer qu'on est en présence d'un OS si n'importe quelle application peut mettre l'ensemble du système à mal?

            Auquel cas, Linux n'est pas vraiment un OS alors, si? Parce qu'il me semble qu'une fork-bombe bien placée arrive (arrivait?) à faire des dégats...
            • [^] # Re: Plop

              Posté par  . Évalué à 2.

              Non, plutot une malloc-bombe, et il parait que c'est resolu. Mais tu as raison : c'etait un comportement totalement inacceptable de la part du noyau Linux.
    • [^] # Re: Plop

      Posté par  (site web personnel) . Évalué à 2.

      >Ca confirme donc mon attitude : mettre autant de free qu'il y a de malloc. La parité free/malloc ne garantie en rien qu'il n'y ait pas de leak, car souvent la libération de la memoire se fait a divers endroits suivant le chemin suivi par le code. Le cas typique:
      int foobar()
      {
          void *pt1, *pt2;
      
          if (!(pt1 = malloc(1))) {
              fprintf(stderr, "holly shit");
              return(-1);
          }
      
          if (!(pt2 = malloc(1))) {
              fprintf(stderr, "holly shit");
              free(pt1);
              return(-1);
          }
      
          /* Faire un truc interessant ici */
      
          free(pt1);
          free(pt2); /* 3 free, mais deux malloc seulement :-) */
      
          return(0);
      }
      
  • # Rapidité

    Posté par  . Évalué à 1.

    A mon avis, les arguments précédents (responsabilisation du programmeur, propreté du codage, extensibilité de l'application) sont parfaitement valables, mais il convient de prendre aussi en compte le problème de l'efficacité.

    Je n'ai pas fait de tests à ce sujet, ni été regarder le code Linux sur ce chapitre, mais je sais que le noyau 2.4, et certainement aussi le 2.6, alloue de la mémoire de façon contigue (zut, je n'ai pas de tréma, désolé...) au programme appelant, dans une zone préréservée à cet effet, du coup, la libération du fait du noyau se résumera à libérer cette zone, ce qui ira vite (en fait, comme cette zone a été réservée de toutes façons, le noyau devra forcément la libérer à la fin de l'exécution du processus), en revanche, si on libère les zones explicitement, on rajoute inutilement du code.

    Bon, évidemment, on pourra me répondre que la terminaison d'un processus n'est généralement pas le moment où l'on a besoin d'un maximum de performances, puisque justement, la tâche est achevée, mais j'ai connu un cas contraire (un processus effectuait un fork(), le fils faisait une tâche complexe, puis revenait, et le père exploitait le résultat).

    Bref, pour moi, si tu cherches des performances, évite ces free().
  • # en passant

    Posté par  . Évalué à 4.

    mettre null dans le pointeur après libération de sa mémoire
    pointée est une bonne manie...
    (ça évite les double free car free(null) ne pose pas de problème)
    • [^] # Re: en passant

      Posté par  . Évalué à 2.

      Oui surtout que free(unpointeurpasnulmaisdejalibere) provoquera un segfault ...
      • [^] # Re: en passant

        Posté par  . Évalué à 2.

        Il est mieux d'avoir un programme qui segfaulte pour qu'on puisse se rendre compte qu'il y a un bug dans l'air, plutot que d'aoiv un programme qui ne segfaulte pas mais qui buggue furtivement derrière ton dos.

        Un jour, ce bug furtif se réveillera à un endroit innatendu, et ce sera beaucoup de temps perdu à débugguer...
        • [^] # Re: en passant

          Posté par  . Évalué à 3.

          bah un assert avant le free c'est plus propre qu'un vilain core:)


          assert(monpointeur!=NULL);
          free(monpointeur);
          monpointeur=NULL;

          avec la macro qui va bien tu peux avoir l'assertion en mode DEBUG et pas en mode Release

          #ifdef DEBUG
          #define MYFREE(ptr) assert(ptr!=NULL); free(ptr); ptr=NULL
          #else
          #define MYFREE(ptr) free(ptr); ptr=NULL
          #endif

          et tu invoques MYFREE au lieu de free
          • [^] # en repassant

            Posté par  . Évalué à 2.

            Il y a un problème avec ta macro.

            Si un autre programmeur fait un truc du genre :

            if (m_ptr->value == 0 ) MYFREE(ptr);
            return m_ptr;

            Ta macro sera développée comme :
            if (m_ptr->value == 0 ) assert(ptr!=NULL);
            free(ptr);
            ptr=NULL;
            return m_ptr;

            Là, tu as une destruction du pointeur quelque soit les conditions.

            Ta macro doit être écrite :
            #define MYFREE(ptr) { assert(ptr!=NULL); free(ptr); ptr=NULL; }

            Mais d'une manière générale, il faut mieux éviter des macros et faire des fonctions inline (et oui, on peut faire du inline en C )
            • [^] # Re: en repassant

              Posté par  . Évalué à 2.

              je prends note de la boulette sur la macro j'avais pas pensé à ce cas.

              par contre je tiens un peu au #define pour différentier les modes debug/release.

              quelques exemples:
              => fonctions de trace que l'on peut virer en mode production/release
              => assertions

              le code non debug ne comprendra pas les appels au fonctions de traces ce qui peut gagner pas mal de temps.




              • [^] # Re: en repassant

                Posté par  . Évalué à 0.

                En mettant des parenthèses dans ta macro, tu évite la boulette:

                #ifdef DEBUG
                #define MYFREE(ptr) (assert(ptr!=NULL); free(ptr); ptr=NULL)
                #else
                #define MYFREE(ptr) (free(ptr); ptr=NULL)
                #endif


                On doit utiliser sans modération les ( ) dans les macros.
  • # L'OS ne fait pas tout ...

    Posté par  . Évalué à 1.

    Et même s'il le fait, le programmeur n'a pas à le savoir. Il ne faut pas se reposer sur son OS, donc, il faut bien libérer la mémoire que l'on a allouée. C'est ce qu'on appelle une bonne pratique dont il faut prendre l'habitude.

    Ou alors, il faut changer de langage de programmation et en utiliser un qui fournit des smart pointers ou un garbage collector ...

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.