Journal Encore une histoire de récupérateur de mémoire

Posté par (page perso) .
Tags : aucun
11
22
juil.
2009
« Il est dit que les programmeurs Lisp savent que la gestion de la mémoire est si importante qu'elle ne peut être laissée aux programmeurs, et que les programmeurs C savent que la gestion de la mémoire est si importante qu'elle ne peut être laissée au système »

Un jour, alors que je développai une application en Javascript et Ruby (des langages de programmation où la gestion de la mémoire est totalement cachée au développeur), mes deux instances de Firefox en sont arrivées à consommer chacune environ 512Mio de mémoire vive (plusieurs jours sans coupure pour Firefox). Sur mes 2Gio, cela fait mal, surtout pour deux onglets souvent actualisés par instance.

L'ordinateur d'à cotés qui a pour différence d'être en 32 bits (ce qui demande moins de mémoire vive) a pour mémoire vive totale 512Mio. Ce constat m'a porté à réfléchir sur le fonctionnement d'un récupérateur de mémoire. Certains feront peut-être le lien avec un déni trollesque que j'ai réalisé dans les réactions d'un article sur le java.

Mais force est de constater qu'une grande quantité de mémoire vive est gaspillée inutilement. Car afficher deux onglets dans une interface demande d'un cotés 512Mio de mémoire, et de l'autre, le dernier mac os bien lourd avec les mêmes deux onglets dans Safari demande 512Mio (à peu de swap près).

Voyons de plus près ce qu'est un récupérateur de mémoire, appelé aussi ramasse miettes, où «garbage collector».

Le but du récupérateur de mémoire est de récupérer les espaces mémoires plus utilisés. Dans un langage de programmation tel que le C, c'est au développeur de gérer les désallocations de mémoires si l'on tient à passer par une gestion manuelle à base de pointeurs. Le compilateur s'occupe de la gestion des variables à l'intérieur des blocs, c'est déjà beaucoup, mais ce restreindre à ce type de gestion de la mémoire est très vite contraignant.

Pour presque toutes les applications en C, la gestion manuelle de la mémoire est inévitable. Et même avec la plus grande organisation, il est presque impossible de ne pas oublier une libération quelque part, ou même de gérer tout les cas possibles que l'on peut avoir.

D'autres langages tels que le ruby (pour ne pas parler du java), ne fonctionnent que par une gestion automatique de la mémoire. Cela a tout de suite pour effet un développement plus rapide et plus simple pour le développeur. Niveau performances, c'est aussi avantageux tant que l'on a de la mémoire disponible, car il n'est pas nécessaire de désallouer les nombreux emplacements alloués.

Le récupérateur de mémoire se déclenche généralement quand il n'y a plus de mémoire disponible, où quand l'application n'a rien de mieux à faire. Apparemment, sur eclipse et firefox, il y a toujours de la mémoire disponible, et l'application a toujours quelque chose de mieux à faire. Mais finalement, ce n'est qu'une question de réglages.

Pour savoir quels zones mémoires sont encore utilisées ou pas, il existe deux grands algorithmes. Je vais redire ce qu'il y a sur wikipedia car j'aime bien mon clavier bépo. Le premier est itératif, et le deuxième récursif.

Pour l'itératif donc, si le langage où le code le permet, les pointeurs sont listés puis parcourus. Les zones mémoires qui n'ont aucun pointeur vers elles sont à désallouer. Si les pointeurs sont inconnus, car le langage ne le permet pas, ou que le code ne le fait pas (en C, c'est souvent réalisé avec des macros), c'est toute la mémoire qui est parcourue. Dès que la valeur d'une zone dans la mémoire pointe vers une autre zone de mémoire, cette zone est marquée comme utilisée. Alors cela peut donner des faux positifs, un nombre peut pointer sans faire exprès une zone de mémoire, mais ce n'est pas bien grave, gérer les faux positifs serait beaucoup plus coûteux en termes de ressources que de ne pas désallouer un objet parmi tant d'autres.

Le récursif passe par un arbre à trois couleurs. Chaque zone de mémoire est représentée par un nœud, et ces nœuds sont reliés par les pointeurs. Au début de l'algorithme, tout les nœuds sont marqués de la couleur blanche.
Les sommets de l'arbre sont des éléments connus, tels que les registres processeurs, la pile d'exécution, mais aussi les constantes, et encore pleins d'autres choses en fonction du récupérateur de mémoire. Chaque nœud traité est passé en noir. Ensuite, les fils du nœud sont récupérés à partir des pointeurs (connus, ou alors comme dans la version itérative, toutes les valeurs du nœud sont parcourues à la recherche d'une adresse valide). Ils sont marqués de la couleur grise, pour ne pas partir en boucle infinie (ce serait dommage). À la fin, les nœuds qui sont restés blanc sont à désallouer.

Que ce soit la version itérative où toute la mémoire est passée en revue, où la version récursive où une représentation de la mémoire en arbre est carrément construite, force est de constater que ce n'est pas une opération rapide pour la machine. Cela m'a permis par exemple de comprendre pourquoi firefox s'amuse à faire swapper la machine avant de se remettre en cause.

Finalement, un récupérateur de mémoire n'est pas si compliqué que ça. Mais face à quelques «delete» bien placés et pas oubliés, ça fait légèrement lourd.

Pour un langage de script tel que le javascript, je pense qu'un récupérateur de mémoire est un outil formidable. Surtout quand on voit les scripts javascript que l'on a souvent sur le net.

Mais pour un langage tel que le C++, l'intérêt est je pense plus faible. Les quantités de mémoires gérées sont très importantes (pour que firefox en arrive à utiliser 512Mio de mémoire, c'est qu'il en voit passé des zones de mémoire), et la complexité de ces algorithmes font que c'est moins efficace.

Certains langages font le pari que le développeur sait ce qu'il fait, et qu'il réfléchit plus et à plus long terme qu'une simple et grosse boucle, alors que d'autres langages masquent cet aspect au développeur en pensant que vue le nombre d'erreurs élevé que ces derniers réalisent, il vaut mieux faire une grosse boucle. Ce qui nous ramène à la citation du début.

PS: Je préfère une application en C qui perd un peu de mémoire sur certaines opérations, à une application java qui consomme 200Mio quoi qu'il arrive. Mais ça porte à débat évidemment.
  • # J'ai eu ce probleme

    Posté par . Évalué à 10.

    Pendant le développement d'une interface graphique en JavaScript, j'ai rencontré ce problème de mémoire (ça montait doucement vers l'infini et au delà :) ). Mais en fait, le problème viens souvent (tout le temps ?) du code JavaScript et pas de l'implémentation de JavaScript.

    Dans mon cas, le problème était :
    un setInterval qui créer un objet XMLHttpRequest à chaque fois, l'objet XMLHttpRequest créer un objet avec les bonnes propriété grâce à au retour XML. Mais l'objet XMLHttpRequest etait toujours atteignable a travers l'objet créé .

    Résolution : mettre l'objet XMLHttpRequest à null ou n'en créer qu'un seul et l'appeler à chaque setInterval (c'est le mieux :) )

    Bref, un garbage collector n'empêche pas d'être con :) et surtout de faire attention mais c'est bien pratique.
    L'avantage des garbage collector de C++, c'est que tu t'en sert EXCLUSIVEMENT si tu le veux. Sinon, tu ne t'en sert pas. Et même si tu t'en sert, tu peut gérer ta mémoire à la main aussi, c'est bien non ?
  • # Pas compliqué, vraiment ?

    Posté par . Évalué à 7.

    J'ai des doutes sur le fait qu'un récupérateur de mémoire ne soit « pas si compliqué que ça ». Entre consommation excessive ou pertes de performances le choix n'est pas toujours simple.

    À titre personnel j'aime bien le chapitre sur ce sujet dans le livre « Développement d'applications avec OCaml ». Il est disponible en ligne ici : http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/book-ora083.html
  • # Intéressant

    Posté par . Évalué à 5.

    Est-ce Firefox ou son "moteur" javascript qui bouffe toutes les miettes?

    Sinon réduire le Java à son ramasse miettes, ce n'est pas sympa. :)

    Question naïve d'un gars qui n'a jamais developpé en C, est-ce qu'une application C un tant soit peu complexe ne fini t-elle pas par intégrer un ramasse miettes en plus du soin apporté à désallouer ses propres allocations?
    • [^] # Re: Intéressant

      Posté par (page perso) . Évalué à 7.

      Réponse rapide : non.
      Tu n'as pas besoin d'un garbage collector puisque tu gères déjà la libération de tes allocations mémoire.
      De plus, il n'est pas forcément si difficile que ça de gérer correctement les besoins mémoire quand on a une bonne compréhension du code, ça demande simplement de la discipline et un peu de temps.
      • [^] # Re: Intéressant

        Posté par . Évalué à 3.

        Tu n'as pas besoin d'un garbage collector puisque tu gères déjà la libération de tes allocations mémoire.

        Bien sûr. Mais dans des cas non-triviaux ça revient souvent à réimplémenter des formes ad-hoc de « garbage collection » (comptage de références, etc.).

        De plus, il n'est pas forcément si difficile que ça de gérer correctement les besoins mémoire quand on a une bonne compréhension du code, ça demande simplement de la discipline et un peu de temps.

        Oui, heu... En pratique, tu oublies toujours une petite déallocation dans un cas particulier, et tu as une belle fuite mémoire qui prendra du temps à débusquer.
        • [^] # Re: Intéressant

          Posté par (page perso) . Évalué à -1.

          Comparer un compteur de références et un GC faut le faire ...
          Dans un cas t'as juste un entier à lire et s'il est à 0 tu vides, dans l'autre cas tu lis toute la mémoire du processus, et t'essaye de deviner ce qu'il va se passer...
          C'est pas "tout à fait" la même chose...
          • [^] # Re: Intéressant

            Posté par . Évalué à 6.

            en quoi le comptage de référence n'est pas un GC ?
            ça dépend de la définition qu'on en a, mais pour mois un GC c'est un moyen automatique de gestion de la mémoire.
            • [^] # Re: Intéressant

              Posté par (page perso) . Évalué à 3.

              Comme je l'ai dit dans mon commentaire, dans un cas tu lis tout (ou une grande partie) de la mémoire à des moments aléatoires, et donc avec des résultats plus ou moins aléatoires, dans l'autre cas, c'est toujours exactement au même moment que ça se passe, et c'est parfaitement prévisible avant même d'essayer. Non franchement le seul rapport que je vois entre un GC et un compteur de référence, c'est que sur les langages d'assez haut niveau on peut utiliser un compteur de références sur chaque objet, et yapluka lire chaque compteur périodiquement. Donc dans un cas on fait du polling avec tous les inconveniaents qui vont avec (charge CPU entre autre), dans l'autre cas on sait exactement quand il faut le faire, et comment, et où.
    • [^] # Re: Intéressant

      Posté par . Évalué à 4.

      Question naïve d'un gars qui n'a jamais developpé en C, est-ce qu'une application C un tant soit peu complexe ne fini t-elle pas par intégrer un ramasse miettes en plus du soin apporté à désallouer ses propres allocations?

      Non mais par contre si t'as pas bien fait attention a ta strategie d'allocation de memoire, tu vas pleurer.

      Si tu n'utilises pas de framework qui te cache une grosse partie de la difficulte et que tu fais ca sans reflechir vraiment, tu vas avoir un programme non maintenable et absolument pas extensible (avec tout un tas de leak en bonus si comme tout programmeur C qui se respecte, tu checkes pas la totalite de tes allocations).

      En gros sans experience, c'est extremement difficile des que ton programme atteint une taille raisonnable. Et pour acquerir de l'experience, y a pas, faut te planter et en chier! En plus selon le type de programme tu vas avoir des strategies d'allocation complement differentes et ton experience est donc pas forcement reutilisable de l'un a l'autre.

      Typiquement pour un gros truc genre jeu ou appli ou tu veux un max de perfs, tu peux gagner beaucoup en allouant un gros paquet au demarrage ou lors du chargement de tes donnees et limiter a fond les allocations dynamiques pendant le jeu. Du coup tu liberes quasi toute ta memoire a des points precis et c'est plus dur de te rater et d'oublier des trucs (plus dur, ca reste quand meme facile de te tirer une balle dans le pied).
      • [^] # Re: Intéressant

        Posté par . Évalué à 1.

        >En gros sans experience, c'est extremement difficile des que ton programme atteint une taille raisonnable.

        Non ça demande de la discipline; un malloc => un free, voir plusieurs selon les cas.
        Si on ne fais pas le free, il faut mettre un commentaire pour expliquer pourquoi (c'est donnée dans une lib qui fera le free elle même)

        Il ne faut pas décorner les boeufs avant d'avoir semé le vent

        • [^] # Re: Intéressant

          Posté par (page perso) . Évalué à 5.

          Et sur une grosse appli, tu arrives à une fragmentation mémoire qui fait qu'au final, tu utilises de plus en plus de mémoire.

          C'était le gros problème de Firefox 2.x. Les libérations étaient bien faites, mais la fragmentation était telle que le système n'arrivait pas à efficacement recycler les blocs libérés.
          • [^] # Re: Intéressant

            Posté par . Évalué à 2.

            firefox utilisait malloc de la libc ou sont propre allocateur (qui utilisait mmap ou sbrk)?

            "La première sécurité est la liberté"

          • [^] # Commentaire supprimé

            Posté par . Évalué à 2.

            Ce commentaire a été supprimé par l'équipe de modération.

            • [^] # Re: Intéressant

              Posté par . Évalué à 2.

              J'adore aussi l'allocation dans la pile. C'est ultra rapide et déalloué automatiquement à la fin du contexte.

              Par contre, comment tu gères une pile trop petite ? J'ai l'impression que tu te prends un "segfault" impossible à récupérer. Je me demande aussi si il existe un moyen simple de fixer la taille d'une pile autrement que par un appel à "clone()". D'ailleurs l'allocation de la pile est un mystère. J'ai l'impression que c'est fait à la demande sur une zone de taille fixe. Donc, c'est soit l'équivalent d'un mmap() de taille fixe, soit l'utilisation d'un mmap avec l'option GROWDOWN.

              "La première sécurité est la liberté"

              • [^] # Commentaire supprimé

                Posté par . Évalué à 2.

                Ce commentaire a été supprimé par l'équipe de modération.

                • [^] # Re: Intéressant

                  Posté par . Évalué à 2.

                  Tu veux dire que la pile est alloué par loader selon des directives d'un shell ? Comme créer un process en C avec une taille de pile spécifié ?

                  Sous Linux, la taille de la pile est fixé habituellement par le shell. Sous windows, c'est fixe dans le binaire (16 Mo, il me semble). Mais comment faire l'opération de fixer une taille de pile sous Linux depuis un autre binaire (qui n'est pas un shell donc).

                  Je note l'histoire de la stack alternaltive.

                  "La première sécurité est la liberté"

        • [^] # Re: Intéressant

          Posté par . Évalué à 2.

          Si c'etait aussi simple que ca, y aurait pas autant de programmes avec des fuites memoire, hein.

          Exemple simpliste mais qui arrive tout le temps: des fois tu recuperes des objets d'une lib quelconque et c'est a toi de liberer la memoire et t'as pas lu la doc correctement. Paf leak et pourtant t'as bien exactement le meme nombre de free et de malloc dans ton source, c'est juste que t'as un malloc dans la lib et pas de free correspondant dans ton code.

          Si tu bosses avec des objets COM (refcomptes) et que t'oublie de dereferencer les objets que tu retournes a ton appelant: tu leakes de la mem (y a pas de malloc ou de free, juste un constructeur et tu dois appeller la bonne methode sur ton instance avant de retourner les objets au programme appelant).
          • [^] # Re: Intéressant

            Posté par (page perso) . Évalué à 2.

            En même temps, si tu lit mal la doc ....
            Quand j'utilise une fonction que je n'ai pas écrite, je cherche un minimum les implications que cela aura sur mon code (dont les implications mémoires).

            Sinon je trouve le code beaucoup mieux que du code Java et aussi plus instructif. Mais bon l'égout et les couleuvres.
          • [^] # Re: Intéressant

            Posté par . Évalué à 2.

            Si c'etait aussi simple que ca, y aurait pas autant de programmes avec des fuites memoire, hein.

            Je ne sais pas mais comme tu le dis toi même, faut lire la doc. Pour avoir bossé sur des gros projet (c++ principalement) les gros soucis mémoire venaient principalement parce qu'on a filé un clavier à des branquinioles. J'ai vu des horreurs du genre
            =======
            char *a = malloc (... )
            ...
            char *b=a;
            free(a);
            return(b);
            ====
            ou plus subtile
            auto_ptr fct_truc(){
            if (truc) return x_;
            return auto_ptr(new machin())
            }
            utilisé ensuite comme suit
            machin &plop=&(*fct_truc());

            des non équivalence entre new[] delete[] (pourtant c'est pas dur)
            un free d'une variable de la pile (non crée par new ou malloc)

            Et une légère fuite mémoire du à la non libération d'une ressource précédemment passée à une lib lorsqu'on la remplaçait (la fonction renvoyait le pointeur passé précédemment et fallait donc le libérer, précisé correctement dans la doc.)

            Enfin heureusement valgrind point tout ces soucis ;) (par contre si y a du versant dans le code attendez vous à du verbeux, cette lib est tout sauf clean)

            Il ne faut pas décorner les boeufs avant d'avoir semé le vent

        • [^] # Re: Intéressant

          Posté par . Évalué à 2.

          Non ça demande de la discipline; un malloc => un free, voir plusieurs selon les cas.

          Oui, dans le cas facile où le pointeur n'appartient qu'à un bout de code localisé. Si c'est un objet (une structure, que sais-je) utilisé par différentes parties du programme avec des besoins en durée de vie différents, ça devient vite beaucoup plus délicat (c'est-à-dire qu'on revient in fine à des méthodes de type comptage de références, c'est-à-dire l'embryon d'un GC).
  • # Comme d'habitude avec yellowiscool

    Posté par . Évalué à 10.

    > Je vais redire ce qu'il y a sur wikipedia car j'aime bien mon clavier bépo

    Le problème c'est que tu as du t'arrêter à la section "basic algorithms"... Les GC de prod, c'est un tantinet moins con et plus optimisé que ça.

    Tu crois vraiment qu'un GC passe son temps à scanner toute la mémoire ? Tu as entendu parlé des GC générationels ? Tu as déjà monitoré un vraie application pour avoir les stats des générations et constater combien de fois on scanner la mémoire en pratique ? Quels étaient les bouts de code pathologique contre un GC ? Comme écrire son application pour aider le GC ( http://developers.sun.com/learning/javaoneonline/j1sessn.jsp(...) ) ?

    Tu sais qu'on peut faire tout ca de manière concurrente ? Qu'on est pas obligé de faire un stop-the-world pour marquer ni pour contacter ? Qu'on est pas obligé de scanner toute la mémoire modulo un petit overhead mémoire ? Qu'on peut faire du GC en temps réel soft et collaborer avec le GC pour minimiser le jitter ( http://qconlondon.com/london-2008/file?path=/qcon-london-200(...) ) ?

    Présentation d'un GC moderne, aka G1:
    slides, http://developers.sun.com/learning/javaoneonline/2008/pdf/TS(...)
    papier, http://research.sun.com/jtech/pubs/04-g1-paper-ismm.pdf

    Est ce que tu as compris que le problème d'un GC, c'est simplement qu'en général il ne va pas réclamer la mémoire dans les veilles générations sauf si il est à cours à mémoire. C'est aussi simple que ca ! C'est pas génant pour certaines applis, pour d'autres si.

    De même tu as beau faire tes jolis malloc/free à la main. Si ton tas devient fragmenté le système ne récupéra pas la mémoire non plus, et bon courage pour compacter à la main...

    Après j'ai donné tout les exemples en Java par ce que tu aimes bien dire n'importe quoi sur Java, mais on peut prendre d'autres exemples.
    • [^] # Re: Comme d'habitude avec yellowiscool

      Posté par . Évalué à 1.

      Qu'on peut faire du GC en temps réel soft et collaborer avec le GC pour minimiser le jitter


      Je suis super déçu :

      "* Essentially a “garbage collector with knobs”"
      Large interrupts are avoided at the cost of small, regular, and
      predictable collections


      En gros, on refait un peu de truc manuel, et on passe plus souvent avec une durée limitée au lieu d'attendre le fait de manquer de mémoire... :/

      Présentation d'un GC moderne, aka G1:

      G1 a l'air plus sympa avec une sorte de typage de la mémoire en jouant sur la probabilité de rester en vie d'une région mémoire.

      "La première sécurité est la liberté"

    • [^] # Re: Comme d'habitude avec yellowiscool

      Posté par (page perso) . Évalué à 1.

      C'est sympa d'ajouter des précisions. Maintenant, t'es pas obligé de le dire sur ce ton non ?

      Donc, non je ne crois pas du tout qu'un GC passe son temps à scanner la mémoire. C'est trop coûteux en performances. J'ai aussi entendu parler des GC générationels. Que l'on peut heureusement faire de la concurrence, et tout et tout.

      Mais je pense que ces choses n'avait pas la place dans mon journal.

      Envoyé depuis mon lapin.

      • [^] # Re: Comme d'habitude avec yellowiscool

        Posté par . Évalué à 7.

        > t'es pas obligé de le dire sur ce ton non ?

        Y'a peut être un relation de cause à effet avec le fait que tu passes ton temps à dire des choses fausses et à t'en servir comme justificatif par ce que t'aime pas un truc ?

        > Mais je pense que ces choses n'avait pas la place dans mon journal.

        Et donc ton journal il sert à quoi ? Tu essais de dire qu'un GC ne sert à rien, que c'est inefficace et que firefox, si les devs n'étaient pas des tocards, ne devrait pas en utiliser.

        Pour justifier l'inefficacité tu te sers de l'argument "scanner toute la mémoire" "scanner plein de région mémoire" et tu présentes de algorithmes avec des mots compliqués comme itératif ou ŕecursif. Mais en réalité ce n'est pas être pas si inefficace que tu voudrais bien le croire... Tu as regardé combien de temps ton firefox passait dans le GC ? Sinon tu parles dans le vide.

        De même tu parles de récupération mémoire et que firefox préfère faire swapper que de réclamer la mémoire. Premièrement tu peux me donner la source de cette info ? Deuxièmement, on t'explique que faire des free ne te libérera pas plus la mémoire si le tas est fragmenté (et avec un appli type browser avec tab ca fragmente très facilement). Bref tu utilises une informations non vérifiable (et que je pense erronée) et une autre fausse...

        Ca serait la première fois, ca passerait. Mais tu le fais à répétition, alors oui je suis sec. Quand tu viendras avec de vrai arguments ca changera peut être. Pour le moment tu as décidé que Java et les GC c'était pas bien, et tu utilises n'importe quoi pour le dire... Si c'est si mauvais que ca, c'est pas très compliqué de venir avec des chiffres vérifiables qui montrent que c'est le cas.


        Au passage tu as raté un des vrais problème lié à l'utilisation d'un garbage collector: La création d'un objet, n'est plus vu comme une allocation mémoire pouvant échouer. Tu vas te prendre une vilaine runtime exception sortie de nulle part, que personne ne gère, si tu es à cours de mémoire. Là ou pouvait traiter tranquillement l'erreur, on se retrouve avec le même problème qu'un programme qui explose sa pile.
        • [^] # Re: Comme d'habitude avec yellowiscool

          Posté par . Évalué à 1.



          Au passage tu as raté un des vrais problème lié à l'utilisation d'un garbage collector: La création d'un objet, n'est plus vu comme une allocation mémoire pouvant échouer. Tu vas te prendre une vilaine runtime exception sortie de nulle part, que personne ne gère, si tu es à cours de mémoire. Là ou pouvait traiter tranquillement l'erreur, on se retrouve avec le même problème qu'un programme qui explose sa pile.



          Juste pour signaler que dans certain langage ayant un GC lèvent une exception qui peut être rattrapée si l'allocation échoue, certain le font aussi avec la pile .

          Mais en général c'est peut utilisé, et peut être inutile car si un GC traçant est utilisé et qu'il y a de l'espace de swap sur le système, alors le programme sera totalement inutilisable avant d'avoir utilisé toute la mémoire.
        • [^] # Re: Comme d'habitude avec yellowiscool

          Posté par (page perso) . Évalué à 1.

          Pour ton premier point, je vois pas ce qui est faux à part certaines approximations au niveau de firefox et eclipse. Je pense l'avoir compris, la pifométrie et certaines personnes, ça fait deux. Désolé, je préfère avoir des commentaires négatifs (mais intéressant quand même) que devoir me coltiner des benchmarks, et donc ne rien faire par flemme.

          Pour ton deuxième point, tu confonds ton interprétation du journal et ce que j'ai voulu faire en écrivant ce journal.

          Il me semble que j'ai participé qu'à un seul débat sur l'utilité du récupérateur de mémoire, mis à part quelques trolls de temps en temps… Le débat a été enrichissant, et j'ai voulu voir un peu plus loin à l'occasion. Je pense que le fonctionnement d'un récupérateur de mémoire n'est pas évident pour tout le monde, et même si je ne connaissait pas grand chose au fonctionnement interne, j'ai pensé qu'expliquer les bases en faisant plus que donner un lien vers wikipedia peut être intéressant pour les lecteurs de linuxfr. Je ne suis pas expert dans le domaine, et tant mieux.

          Il ne faut pas voir une volonté de vouloir denier une technologie et d'imposer sa façon de penser chez tout le monde. Me ficher comme le couillon de service qui ne peut que denier sans rien connaître, juste parce que j'ai osé critiquer l'utilité à mes yeux d'un récupérateur de mémoire, est assez pénible. Je peux dire des choses fausses comme tout le monde, j'en ai peut-être dit un peu plus que tout le monde sur la troll java il y a quelques semaines, mais me ficher, c'est énervant.

          Comme tu le dit, j'aurais écrit ce journal avec un autre pseudo, tu ne m'aurais pas attaquer de la même façon. C'est regrettable je pense.

          Envoyé depuis mon lapin.

          • [^] # Re: Comme d'habitude avec yellowiscool

            Posté par . Évalué à 0.

            Me ficher comme le couillon de service qui ne peut que denier sans rien connaître, juste parce que j'ai osé critiquer l'utilité à mes yeux d'un récupérateur de mémoire, est assez pénible.

            Pourtant, « le couillon de service », c'est bien l'image que tu donnes quand on lit ce message. Le coup de « j'ai la flemme d'apporter des arguments factuels alors je préfère raconter n'importe quoi plutôt que de me la fermer », chapeau.
            • [^] # Re: Comme d'habitude avec yellowiscool

              Posté par (page perso) . Évalué à 2.

              Sauf que ce n'est pas ça. J'écris des trucs que je pourrais prouver sans avoir envi de le faire parce que je pense pas que ce soit important.

              Toi tu veut une preuve faite longuement et scientifiquement pour par exemple le fait que firefox a tendance à faire swapper mon ordinateur.

              Désolé je suis en vacances, et j'en ai rien à foutre. Je préfère aller à la plage, ce que je m'apprête à faire :-)

              Envoyé depuis mon lapin.

              • [^] # Re: Comme d'habitude avec yellowiscool

                Posté par . Évalué à 1.

                ben le probleme, c'est que t'as deja longuement trolle sur ce sujet recemment.

                Tu ne maitrisais visiblement pas *du tout* ton sujet, a pondu un code a la mord moi le noeud pour un benchmark fondamentalement faux, parce que tu ne maitrisais justement pas *du tout* ton sujet.

                Ca t'as ete explique en long en large et en travers, on t'as explique qu'une vm c'est complexe, le gc est tres evolue et regle avec precision, que pour en parler objectivement, faut se renseigner et faire un peu plus que de lire des on dit ou une page vikipedia expliquant de loin les grandes lignes du principe de fonctionnement simplifie a l'extreme de la philosophie generale de l'idee globale d'un resume reducteur du fonctionnement du gc (ca va, j'ai suffisament insiste sur le fait que la page que tu as lu n'est qu'un tres bref resume tres loin des details techniques qui changent enormement de chose dans les differentes implem?).

                Ton journal se contente de faire un resume du resume du resume, pour lacher derriere un vilain troll sur le GC, ca fait plusieurs fois que tu racontes des grosses conneries, et tu continues a nous faire le coup de l'etudiant innocent, plein de certitudes, de "j'ai eu un amphi de 3h00 la dessus, alors, bon, hein, je sais mieux que vous de quoi je parle" etc.

                Que tu critiques l'utilite d'un GC par manque d'experience flagrant en developpement, parce que il est vrai qu'instinctivement le gc a l'air tres gourmand, ca se comprend.
                C'est la qu'on t'explique que non, ca marche pas comme ca, bla bla, la t'es cense soit prendre ca pour argent comptant (ce que tu n'as pas fait), soit te renseigner en profondeur (ce que tu n'as pas fait non plus).
                Et tu reviens avec ton bon vieux point de vue etudiant "oue le C c'est mieux, c'est plus l33t, c'est un mec de maitrise qui me l'a dit".
                Et ca oui, j'appelle ca etre un couillon de service.
                • [^] # Re: Comme d'habitude avec yellowiscool

                  Posté par (page perso) . Évalué à 0.

                  Merci de m'avoir ouvert les yeux. C'est vrai que sans faire de thèse ou bosser 10 ans sur le sujet, on ne peut pas avoir d'opinions sur un sujet. Vive l'informatique !

                  Envoyé depuis mon lapin.

                  • [^] # Re: Comme d'habitude avec yellowiscool

                    Posté par . Évalué à 2.

                    Bah personne t'empeche d'avoir ton opinion sur la question, c'est juste que si tu tiens a la partager avec le monde entier, faut pas t'etonner que ca te revienne dans la figure...

                    Mieux vaut ne rien dire et passer pour un con que de l'ouvrir et ne laisser aucun doute.
  • # Fausse idée sur les garbages collectors

    Posté par . Évalué à 7.

    Dans un programme comme Ruby, Java ou autre, c'est au programmeur de gérer la mémoire lui même. C'est juste que 97% du travail est automatisé. Pour libérer un objet en mémoire il suffit de le déréférencer. Biens ur on peut accélérer les choses avec un destroy() quelconque mais généralement le simple fait de détruire les références vers l'objet suffit à faire que le garbage collector fera le ménage tout seul.

    Ca marche très très bien.

    En C c'est plus compliqué. Il faut spécifiquement libérer toute la mémoire que l'on a consommé. C'est long, c'est réberbatif, c'est casse pied et si on oublie quelquechose ca peut trainer en mémoire jusqu'à la fermeture du programme.

    Le SEUL avantage de l'accès mémoire direct au point de vue programatique (ie si on considère le surcout en mémoire du GC comme négligeable) est dans un cas bien précis :

    Si on objet a un accès passif à une ressource système (par exemple il écoute sur un port X)
    - En C : je sors le fusil à pompe, pan -> objet détruit. Je peux immédiatement relancer un autre objet sur le port X
    - En programmation GC : Je sors le pot de peinture rouge et je peints une jolie croix sur le dos de mon objet. Si j'essaye de relancer immédiatement un autre objet sur le port X je risque de me prendre une erreur de type "t'es mignon, mais le port X est occupé". Tout çà parceque le GC il dort, ou qu'il est déjà en train de donner des coups de fusil à pompe ailleurs. Je suis donc dépendant de son bon vouloir.
    • [^] # Re: Fausse idée sur les garbages collectors

      Posté par . Évalué à 5.

      Le SEUL avantage de l'accès mémoire direct au point de vue programatique [...]

      Il y a un autre cas où la gestion directe est quasiment indispensable: pour le temps-réel et tout ce qui s'en rapproche. Par exemple un synthé audio, ou bien un moteur de rendu pour un jeu.

      Sinon je suis entièrement d'accord, quelque soit l'approche utilisée il faut accorder de l'attention à l'utilisation de la mémoire.

      J'ajouterai que en C/C++, la gestion mémoire ne se limite pas à apparier malloc et free; regrouper les allocations, réutiliser ce qui est déjà alloué, utiliser l'allocation sur le tas sont tout aussi importants.
      • [^] # Re: Fausse idée sur les garbages collectors

        Posté par . Évalué à 4.

        Il y a un autre cas où la gestion directe est quasiment indispensable: pour le temps-réel et tout ce qui s'en rapproche. Par exemple un synthé audio, ou bien un moteur de rendu pour un jeu.

        Alors en temps réel "dur" : ie garantie de temps de réponse (j'appuie sur le bouton et dans moins de cent cycles CPU il se passe quelquechose) le plus souvent oui. Encore que généralement pour faire du vrai temps réel dur il faut un kernel temps réel, ce qui implique pas mal de choses au niveau de l'architecture de l'OS lui même et du matériel sous-jacent.

        Mais en temps réel "mou" : ie garantie de temps d'éxecution (j'ai 200 threads qui tournent, je veux que tous les calculs soient finis dans 200ms) ca devient beaucoup plus discutable. En effet au niveau allocation mémoire les questions de type "ai-je le temsp de lancer un appel système pour libérer l'espace mémoire ?", "dois-je défragmenter la mémoire pour gagner des perfs ?", "puis-je utiliser un segment mémoire déjà dispo mais trop grand pour moi, ou dois-je en reserver un autre ?" etc. sont légions. Et très vite le besoin d'un arbitre se fait sentir (ben oui 200 threads qui discutent les uns avec les autres au niveau perf c'est moyen...). Et le garbage collector, quand il est bien conçu (et conçu dans cette optique) est un arbitre absolument fantastique. Avec la multiplication des coeurs (et donc des threads) je suis prèt à parier qu'il va devenir de plus en plus dur pour les programmeurs C de battre les GC dans une optique temps réel mou. Donc les jeux et les logiciels d'édition musicale risquent d'avoir recours à des GC de plus en plus souvent.
        • [^] # Re: Fausse idée sur les garbages collectors

          Posté par . Évalué à 1.

          Je tiens à préciser: je ne parle pas de moteurs de jeu (donc beaucoup sont déjà écrits en grande partie en langage interprétés, donc avec GC), mais de moteurs de rendus.

          En ce qui concerne les perfs des appels systèmes d'allocation mémoire, tu as tout à fait raison, c'est bien pour ça qu'on ne les utilises pas lorsqu'on a besoin d'être vraiment rapide. On alloue tout en bloc et on réutilise les mêmes zones de mémoires autant que possible. C'est en partie faisable avec un GC, mais en partie seulement, et cela revient à réinventer un système d'allocation manuelle pour empêcher que le GC ne se déclenche.

          Je ne dit pas que c'est impossible, mais actuellement je n'ai connaissance d'aucun moteurs de rendus, ni d'aucun moteurs audio, qui utilise un GC. Et en ce qui concerne les premiers, la tendance me semble au contraire à être de plus en plus proche du hardware.
          • [^] # Re: Fausse idée sur les garbages collectors

            Posté par . Évalué à 2.

            On alloue tout en bloc et on réutilise les mêmes zones de mémoires autant que possible. C'est en partie faisable avec un GC, mais en partie seulement,

            Euh... En ce qui concerne Java, Erlang ou .Net il y a moyen très facilement de préciser la mémoire initiale, la mémoire minimum et la mémoire maximum utilisée. Si les trois paramêtres sont identiques on exactement un comportement d'allocation bloc sans pour autant avoir besoin de réinventer la roue. En java on peut même au démarrage d'une appli définir quasiment à la virgule prèt le comportement des générations et de l'eden, et donc avoir une application qui ne va allouer de la mémoire que quand il va vraiment y en avoir besoin, et libérer la mémoire que quand vraiment ca ne sert plus à rien.

            Ca se fait en une petite ligne de paramètres, et comble du comble ca se fait après que le programme ait été compilé, et donc un non progammeur peut corriger ces paramêtres au cas par cas en fonction de l'usage qui est fait du programme.

            Je ne dit pas que c'est impossible, mais actuellement je n'ai connaissance d'aucun moteurs de rendus,

            En moteur de rendu temps réel, il y a trois domaines à ma connaissance
            - l'imagerie médicale
            - l'analyse electronique des structures
            - le maquettage démo des grosses sociétés industrielles.

            Dans ces trois cas il s'agit de matériels spécifiques, le plus souvent basés sur des clusters de processeurs vectoriels ou sur des batteries de DSP. Le fait qu'ils soient proches du hardware s'explique assez simplement par la relation un outil => un usage. Pas la peine de se casser les pieds à coder un garbage collector et de l'enrober dans un langage de programation alors qu'au final de toute façon il n'y aure jamais qu'une seule application qui tournera sur la machine. On fait du spécifique. La question ne se pose pas vraiment....
            • [^] # Re: Fausse idée sur les garbages collectors

              Posté par . Évalué à 2.

              On ne parle pas du tout de la même chose... Je parlais des moteurs de rendus utilisés principalement dans le domaine du jeu vidéo, et des moteurs utilisés dans les synthés audio.

              (Peut-être que ça en agace certains d'utiliser la qualification "temps-réel" pour ces usages, mais il faut bien faire une distinction avec les applis de type bureautique; et la distinction est qu'il y a bien, effectivement, une contrainte de temps, même si elle n'est pas "dure" comme pour les applications plus critiques.)

              Dans ces domaines là, je n'ai pas connaissance d'une quelconque tendance à utiliser un GC. Il s'agit peut-être de frilosité, mais je pense surtout qu'on se situe à la limite des capacités du matériel utilisé (sur lequel on a aucune influence), et que la moindre perte de performance coûte.

              En ce qui concerne l'explication sur java, cela me semble rejoindre ce que je disais: au final, on en revient à tout gérer à la main, donc à court-circuiter le GC pour les partie" hautes-performance".

              Je tiens à préciser que je ne dénigre absolument pas les GC, j'ai longtemps été un apôtre d'Eiffel... Seulement je crois que ce n'est pas forcément un outil adapté à toutes les situations.
              • [^] # Re: Fausse idée sur les garbages collectors

                Posté par . Évalué à 2.

                Je parlais des moteurs de rendus utilisés principalement dans le domaine du jeu vidéo

                Bon alors c'est bien ce qu'il me semblait. Juste pour information les API DirectX 7 et plus ainsi que OpenGL (et je ne parle pas des pilotes) utilisent pas mal de techniques très proches du comportement d'un GC. Les problematiques sont différentes mais même si le programmeur du jeu en lui même a l'impression de gérer la mémoire de son appli tout seul comme un grand, derrière le rideau c'est la foirefouille. Les polygones sont tordus, mappés, alloués, transformés et détruits de façon quasiment transparente (et ca tombe bien, parceque gérer quelques millions de polygones à la main un par un c'est lourd).
                On ne peut pas vraiment appeler ca un garbage collector mais dans les faits le programmeur utilise une API de haut niveau (en descendant parfois un peu sur certains points) et derrière le système se démmerde tout seul pour gérer les buffers et la mémoire video.

                au final, on en revient à tout gérer à la main
                Pas du tout. On ne gère pas la mémoire à la main en définissant les pointeurs et les zones mémoires et en les manipulant à la pince à épiler, on donne des instructions au GC pour qu'il se comporte de telle ou telle façon.
                De façon générale il y a trois "segments" mémoire gérés par le GC
                - Les nouveaux arrivants : ce sont les objets fraichement créé. Si ils viennent d'être créés et que le compilateur byte code ne fait pas n'importe quoi, c'est pas al peine de tester pour savoir si il faut les détruire.
                - les objet normaux : ce sont ceux qui sont là depuis un petit moment, mais qui peuvent être déréférencés n'importe quand
                - les vétérans : ce sont les objets qui sont en mémoire depuis un long moment et dont on commence à se dire qu'il y a peu de chance qu'ils partent à la poubelle comme çà.

                Après on donne des règles au GC
                Règle 1 : tous les combiens de temps on déclenche un passage du GC
                Règle 2 : au bout de combien de passage du GC un objet neuf passe dans la case "normal"
                Règle 3 : au bout de combien de passage du GC un objet normal passe dans la case "vétéran"
                Règle 4 : Tout les combiens de passages du GC doit on évaluer les objets de la case "vétéran"

                Quatre paramètres + trois pour définir la mémoire initiale, maximale et minimale...

                C'est très loin d'une gestion mémoire "à la main"

                Seulement je crois que ce n'est pas forcément un outil adapté à toutes les situations.

                Les garbages collectors ont étés mis à disposition du public en même temps que windows 95. Et de fait il souffrent ennormément d'une réputation de gaspilleurs de mémoire. Ce qui est vrai, mais qui n'est absolument plus grave aujourd'hui.
                A part pour les applications ou l'accès au ressources en direct est primordial (noyeau, temps réel dur, deadlocks trop complexes à gérer en mode processus etc.) le garbage collector va faire un boulot fantastique tout en simplifiant grandement la vie du programmeur au prix de 30% de mémoire occupé en plus.

                Bine sur c'est mon opinion, mais vu le chemin que prennent les architectures modernes (plétores de coeurs et mémoire par giga entiers) il va devenir très dur de battre les langages à GC avec les langages traditionnels. Dans des situations de forte charges, le machines virtuelles atteignent déjà les perfs des applications compilées en natif. Erlang par exemple est à peu près imbattable en terme du nombre de connexions par seconde qu'il peut accepter et traiter...
                • [^] # Re: Fausse idée sur les garbages collectors

                  Posté par . Évalué à 2.

                  On ne peut pas vraiment appeler ça un garbage collector mais dans les faits le programmeur utilise une API de haut niveau (en descendant parfois un peu sur certains points) et derrière le système se démmerde tout seul pour gérer les buffers et la mémoire vidéo.

                  Sauf que:

                  - La véritable gestion mémoire se fait à l'intérieur des buffers alloués par des appels OpenGL; et là c'est le développeur qui y organise ses données, et y accède par un système de pointeur (relatif au début du buffer). L'api "de haut niveau" n'est pratiquement utlisée que pour réserver de la mémoire.

                  - OpenGL fournit des garanties sur quels appels peuvent déclencher une allocation, et quels appels ne le feront pas;

                  - le système "automatique" que tu décris pour les buffers consiste essentiellement à incrémenter/décrémenter un compteur pour chaque buffer, sachant qu'ils ne peuvent pas avoir de références circulaires; on est très loin d'un GC moderne; (par contre les buffers peuvent en théorie être déplacés)

                  - les extensions récentes (par exemple les uniform buffers d'openGL 3.1) cherchent souvent à donner un accès de plus en plus fin à la mémoire GPU;

                  La seule possibilité que je vois pour pouvoir intégrer un GC dans un moteur de rendu (toujours dans le contexte du jeu vidéo), ce serait de pouvoir mettre une borne supérieure sur son temps d'exécution à chaque frame. Est-ce possible sur certains GC?
                  • [^] # Re: Fausse idée sur les garbages collectors

                    Posté par . Évalué à 2.

                    La véritable gestion mémoire se fait à l'intérieur des buffers alloués par des appels OpenGL; et là c'est le développeur qui y organise ses données, et y accède par un système de pointeur (relatif au début du buffer).

                    Estc-e que tu connais réellement des développeurs qui réorganisent leurs display lists à la main à chaque frame ? Des dev qui choississent vertex par vertex et pixel par pixel ou appliquer leurs shaders ? Des devellopeurs qui réécrivent toutes les fonctions de Z Buffer et de masking ?
                    Moi pas (Bon si sur Quake 3 Carmack a recodé toute la fonction de Z buffer mais a) sont algo Z aliase sur les grande distances et b) c'est Carmak)

                    le système "automatique" que tu décris pour les buffers consiste essentiellement à incrémenter/décrémenter un compteur pour chaque buffer

                    Non non, le système que je décris et celui qui se met en branle sur chaque buffer et dans chaque display list pour refaire le Z-Sorting, le meshig et autre anti aliasing intra et extra modèle.

                    par exemple les uniform buffers d'openGL 3.1

                    Les uniform buffers servent à passer des variables uniform aux shaders sans avoir à se refrapper de relancer des GLUniform dans tous les sens. C'est typiquement un cas ou on laisse le pilote se démerder parceque ca fait chier d'appeler sans arrêt une fonction à la main. J'ai du mal à voir en quoi ca rapproche du GPU.

                    Il y a des fonctions qui rapprochent du GPU, mais elle sont presque toutes tournées vers le GCGPU. Après il y a des fonctions qui rajoutent plus de finesse sur le controle des shaders, mais là c'est le pilote qui fait le boulot, au niveau du GPU à proprement parler on est ni plus près ni plus loin.

                    ce serait de pouvoir mettre une borne supérieure sur son temps d'exécution à chaque frame. Est-ce possible sur certains GC?

                    Ca ne pose aucun problème d'évaluer à priori le temps d'éxecution d'une fonction par un GC (le GC Erlang le fait très bien). Pour pouvoir adapter çà à une carte graphique, il faudrait d'abord qu'il existe une carte graphique qui possède une telle fonction. A ma connaissance il n'y en a aucune sur le marché. Toutes les cartes graphiques mettent plus ou moins de temps à rendre une image en fonction de la complexité de la scène. Comme en plus toutes les cartes graphiques sont différentes, et qu'une même carte graphique va avoir des comportements variant du tout au tout d'un pilote à l'autre, je ne vois pas comment garantir un temps d'éxecution par frame. (En plus l'industrie du benchmark de carte vidéo par les sites en lignes/journaux en serait détruite. Ce serait dommage, c'est tellement drôle)
                    • [^] # Re: Fausse idée sur les garbages collectors

                      Posté par . Évalué à 0.

                      Estc-e que tu connais réellement des développeurs qui réorganisent leurs display lists à la main à chaque frame ?

                      A ma connaissance, plus personne n'utilise les display lists dans le monde du jeu vidéo: tout passe par des VBO. Et, oui, le contenu de ces VBO est géré par le développeur.

                      Je ne comprends pas pourquoi tu parles de gérer les shaders vertex par vertex, où de réécrire les "fonctions de z-buffer". Quel est le rapport avec la gestion mémoire? Le seul impact que je vois c'est qu'il faut souvent regrouper les vertex par shader, et c'est en grande partie pour ça que l'on doit optimiser le contenu des VBO à la main.

                      Les uniforms buffers permettent surtout d'allouer un bloc de mémoire GPU et de choisir ce que l'on va y stocker, afin d'optimiser le transferts des uniforms entre le CPU et le GPU. Cela en lieu et place d'une allocation transparente gérée par l'API. Donc oui, on est dans plein dans le vif du sujet: passer d'une gestion automatisée à une gestion manuelle.

                      Enfin, "évaluer à priori le temps d'exécution" d'un GC ne suffit pas. Comme tu le dis, les performances des moteurs de rendus sont fonction de la complexité de la scène. Si un GC n'est pas capable de garantir que pour une scène de telle complexité son temps d'exécution sera inférieur à une valeur donnée, cela revient à autoriser la dégradation du framerate par un facteur inconnu...
                      • [^] # Re: Fausse idée sur les garbages collectors

                        Posté par . Évalué à 3.

                        A ma connaissance, plus personne n'utilise les display lists dans le monde du jeu vidéo: tout passe par des VBO. Et, oui, le contenu de ces VBO est géré par le développeur.

                        Alors soit on ne parle pas de la même chose, soit tu le fais exprès. Que l'on soit en vertex array, en display list, en VBO ou en PBO de toute façon le contenu est géré à la main. Les VBO sont plutôt une fosi de plus une prise de distance vis à vis du GCU par rapport aux vertex array qui devaient être renvoyés au serveur en permanence.
                        Le GROS avantage des VBO est que le developpeur, une fois qu'il a optimisé ses vertex n'a plus qu'à donner au pilote des indications d'usage du VBO et qu'ensuite la carte graphique se charge de placer et de déplacer la zone mémoire pratiquement sans intervention du dev.
                        C'est exactement ce à quoi je pensais end isant qu'on a pas vraiment des GC car la problématique est différente, mais qu'on a quand même des systèmes qui y ressemblent furieusement.

                        Je ne comprends pas pourquoi tu parles de gérer les shaders vertex par vertex, où de réécrire les "fonctions de z-buffer". Quel est le rapport avec la gestion mémoire?

                        Le rapport est que toutes ces opérations provoquent un bon nombre de transformations en mémoire. Trier les vertexs pour éviter de transformer des segments inutiles, mapper les vertexs dans les buffers pour préparer à l'affichage, rappler les textures et les PBO pour y appliquer les pixels shaders qui vont bien etc. Une trè grosse partie de tout celà est transparente, soit géré par le pilote OpenGL lui même, soit directement en hard par la carte. Sans parler des couches d'anti-aliasing, de décompression de texture et autres culling.
                        Même si OpenGL offre des méthodes pour toucher à tout celà, une grosse partie du traitement reste souvent en automatique.

                        Les uniforms buffers permettent surtout d'allouer un bloc de mémoire GPU et de choisir ce que l'on va y stocker, afin d'optimiser le transferts des uniforms entre le CPU et le GPU. Cela en lieu et place d'une allocation transparente gérée par l'API. Donc oui, on est dans plein dans le vif du sujet: passer d'une gestion automatisée à une gestion manuelle.

                        A l'époque du T&L on était en gestion automatisée. Quand on est passé au shaders (et donc au moment ou il fallait renvoyer les info transformée au serveur) totu se faisait à la mano. Maintenant avec les Uniform Buffer on peut allouer des blocs qui seront raffraichi automatiquement sur des évènements. Il est normal que s'ait été automatique au moment du T&L car on ne transformait pas les polygones/pixels. On avait pas les outils pour. Depuis qu'on les a les cartes graphiques font à chaque géénration un peu plus de choses en automatique.

                        Si un GC n'est pas capable de garantir que pour une scène de telle complexité son temps d'exécution sera inférieur à une valeur donnée, cela revient à autoriser la dégradation du framerate par un facteur inconnu...

                        Mais cite moi un outil capable de garantir ce temps d'éxecution ! Un outil capable de dire qu'il aura la priorité kernel, que le swap ne se déclenchera pas, que l'anti virus ne fera pas des siennes, que la température ne forcera pas le CPU/GPU à ralentir, que le pilote ne prendra pas de libertés avec le rendu demandé (NVidia, on t'aime), que l'utilisateur ne forcera pas des options graphiques à la con (ATI, on t'aime aussi), qu'il n'y aura pas de micro corruption des buffer suite à un refresh à la barbare et j'en passe.

                        En plus un GC moyennant un surcout en mémoire est beaucoup plus à même de garantir un temps d'éxecution qu'une gestion native. Surtout sur des traitements de type DSP.
                        • [^] # Re: Fausse idée sur les garbages collectors

                          Posté par . Évalué à 0.

                          Bon, soit tout ce que j'ai appris sur les GPUs modernes est faux, soit on ne parle pas de la même chose. Peut-être la situation est-elle différente entre le monde du jeu vidéo et les autres domaines, tel que l'imagerie médical à laquelle tu as fait allusion.

                          Je ne sais pas trop si c'est intéressant de continuer le débat, j'ai l'impression qu'on tourne en rond.

                          Peut-être que les années à venir me donneront tort, qu'on verra l'arrivée de systèmes de plus en plus haut niveau pour la gestion mémoire dans ce domaine. Pour l'instant je ne vois pas cette tendance.
      • [^] # Re: Fausse idée sur les garbages collectors

        Posté par . Évalué à 3.

        Il y a aussi le cas de l'embarqué, du calcul intensif/haute performance ... Bref tous les cas où les ressources sont considérées comme « rares » ou critiques.
  • # Deux articles intéressants

    Posté par . Évalué à 3.

    Je me permet de conseiller à ceux qui sont intéressés la lecture de ces deux articles intéressants de Nhat Minh Lê sur le fonctionnement d'un GC basique :
    http://www.siteduzero.com/tutoriel-3-36456-problematique-et-(...)
    http://www.siteduzero.com/tutoriel-3-34906-recyclage-progres(...)

    C'est bien expliqué, avec des schémas et tout ce qu'il faut pour bien comprendre le principe :) .
  • # Valgrind est ton ami

    Posté par (page perso) . Évalué à 8.

    Un magnifique outil de profiling et debugging. Il détecte entre autres les débordements, les désallocations oubliées et j'en passe ... ;-)
  • # Mouais

    Posté par . Évalué à 5.

    Sinon il y a la solution de ne pas faire d'allocations dynamiques.

    Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

  • # libération mémoire

    Posté par . Évalué à 4.

    Contrairement à ce que tu pourrais penser, lorsque tu fais un free(), la mémoire n'est pas forcément libérée (i.e. retournée à l'OS, par exemple avec brk()).
    La plupart des implémentations se contentent de marquer les blocs libérés comme disponibles pour d'autres malloc().
    Exemple
    $ cat test.c
    #include <stdlib.h>
    #include <stdio.h>

    #define NB_CHUNKS 10024


    int main(int argc, char *argv[])
    {
    int i;
    void *tab[NB_CHUNKS];
    char line[BUFSIZ];

    puts("Before malloc");
    fgets(line, sizeof(line), stdin);

    for (i = 0; i < NB_CHUNKS; i++) {
    tab[i] = malloc(1024);
    }

    puts("After malloc");
    fgets(line, sizeof(line), stdin);

    for (i = 0; i < NB_CHUNKS; i++) {
    free(tab[i]);
    }

    puts("After free");
    fgets(line, sizeof(line), stdin);

    return 0;
    }


    Donne:
    $ cat /proc/3130/maps | grep heap (avant malloc())
    $ cat /proc/3130/maps | grep heap (après malloc(), avant free()
    08557000-08f44000 rw-p 08557000 00:00 0 [heap]
    $ cat /proc/3130/maps | grep heap (après free())
    08557000-08578000 rw-p 08557000 00:00 0 [heap]


    On voit que après malloc(), on a un tas de 08557000-08f44000 = 10407936 (grosso modo 10024*1024 plus deux/trois trucs).
    Après free(), 08557000-08578000 = 135168
    • [^] # Re: libération mémoire

      Posté par (page perso) . Évalué à 1.

      Tu es le premier à parler de malloc(). Peut-être que le monsieur pensait à utiliser mmap() en C...

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

  • # C++ avec Qt gère la mémoire pour nous

    Posté par (page perso) . Évalué à 6.

    Bonjour,

    Je tiens a préciser que Qt fourni un truc de vraiment très bien : il gère la mémoire pour nous. C'est simple : on crée un QObject, et on peut lui ajouter des enfants (contrôles d'une fenêtre, éléments d'une page web, etc). Quand on supprimera le QObject parent, tous les enfants seront supprimés, récursivement.

    Plus besoin de delete ! Et en plus, c'est "instantané" (donc on attend pas qu'un GC se dise «ah tiens, il faut de la mémoire»), et c'est très rapide (pas besoin de parcourir la mémoire, ou de construire un arbre, il est déjà contruit.


    QObject *parent = new QObject();
    QObject *enfant1 = new QObject(parent);
    QObject *enfant2 = new QObject(parent);
    QObject *petitEnfant = new QObject(enfant2);

    //Traitement (les QObject peuvent être des widgets, des chaînes de caractère, etc)

    //On libère le tout
    delete parent;


    Généralement, on n'a même pas besoin du "delete parent;", car un code ressemble souvent à ceci :


    Fenetre fenetre;
    fenetre.show();

    //Fin de procédure, fenêtre libéré (puisque sur la pile), et enfants libérés aussi

    Fenetre::Fenetre() : QWidget()
    {
    //Créer plein d'enfants
    }


    C'est-y pas beau ? C'est un des multiples services rendus par Qt. Je ne sais pas si Glib sait faire la même chose avec GObject, mais j'en doute un peu. (vous voyez, même moi je sais retenir un troll GNOME/KDE, clairement à l'avantage de KDE en plus)
  • # GC et déférencement

    Posté par (page perso) . Évalué à 3.

    Le problème est plus simple que ça en fait.
    La GC est là pour nettoyer la mémoire qui n'est plus référencée et ceci selon des algorithmes qui permettent de le faire de façon optimale. Et le terme plus référencée a une grande importance : le développeur doit toujours se préoccuper de la mémoire allouée ; autrement dit déférencer les objets inutilisés. Ce qui est très important dès que l'on manipule des collections/conteneurs.

    Les problèmes de mémoires que l'on observe souvent avec les applis sur VM proviennent d'oublis de déférencement de la part du développeur.
    • [^] # Re: GC et déférencement

      Posté par . Évalué à 1.

      "déférencer" cela ressemble beaucoup au free() ou au delete(), non ?

      "La première sécurité est la liberté"

      • [^] # Re: GC et déférencement

        Posté par . Évalué à 3.

        Ca ressemble pas mal, mais les problématiques restent plus simples en général parce que les données sont souvent déréférencées "naturellement".

        Les cas tordus où on doit, même avec un GC, gérer la mémoire soi-même en déréférençant auraient donné un code similaire en C/C++. Par contre, tous les autres cas plus standard sont gérés par le GC, ce qui enlève une grosse source d'erreurs potentielles.


        La gestion de certaines tâches est également facilitée. Je pense par exemple à la gestion d'un cache qu'on peut faire à l'aide de WeakRef. Il faut penser à ne pas mettre des références "normales" mais à part ça, on n'a pas de tâche spécifique à réaliser et on voit bien l'intérêt du GC dans ce cas précis : le cache est vidé si on a besoin de mémoire, ce qui est difficile à faire à la main.
      • [^] # Re: GC et déférencement

        Posté par . Évalué à 5.

        Ben non, justement. Dès qu'un objet est utilisé en plusieurs endroits, libérer une référence n'implique pas forcément un free(). Le GC est là pour décider quand appeler free() (ou son équivalent).
        (sans compter que le GC détectera aussi les références cycliques)
  • # C / C++ sont très différents sur ce point

    Posté par . Évalué à 5.

    Au niveau de la gestion de la mémoire, C et C++ sont très différents en pratique, même si ils le sont peu en théorie.

    En C, il n'y a que la discipline et les debogueurs pour éviter les fuites.

    En C++, il y a pas mal de possibilité pour avoir une gestion un tant soit peu automatisée. Les destructeurs permettent de libérer la mémoire proprement : le code est centralisé (dans le destructeur donc), et est appelé dans tous les cas nécessaires (y compris remontée d'exception).

    Tout ce qui est pointeur peut être encapsulé dans une classe mère dont le destructeur libèrera automatiquement la ressource en fin de portée. Ce sont des habitudes à prendre, mais on a là une partie des avantages des avantages d'un gc avec un bon contrôle de ce qui se passe.

    Avec boost on a toute un panoplie de pointeurs intelligents (généralisation du point précédent).

    Le pattern singleton a aussi un intérêt dans ce genre de situation :: [[http://fr.wikipedia.org/wiki/Singleton_%28patron_de_concepti(...)]]

    En fait, C++ amène tant de choses pour améliorer l'approche de la gestion de la mémoire qu'en pratique c'est beaucoup plus fiable.
    • [^] # Re: C / C++ sont très différents sur ce point

      Posté par (page perso) . Évalué à 2.

      Ce concept a un nom, c'est le RAII, un concept d'ailleurs bien pratique pour l'allocation d'objets sur la pile.

      Mais le plus gros avantage de C++ selon moi sur ce plan c'est de pouvoir mixer facilement les différents modes d'allocation et de libération de mémoire. Utilisation de la pile, automatisation avec les pointeurs intelligents (scoped_ptr, shared_ptr et weak_ptr dans boost, mais restant simples à implémenter "a la mano"), ou bien gestion manuelle comme en C, pour faire sa petite cuisine optimisée.
      • [^] # Re: C / C++ sont très différents sur ce point

        Posté par . Évalué à 3.

        Je suis bien d'accord avec l'avantage que tu cites.

        Pour le RAII, j'ai toujours beaucoup apprécié cette façon de faire : cela mixe très bien les avantages de l'allocation par la pile et permet de ne rien avoir à faire pour bien gérer les exceptions, tout ça sans fuite de mémoire et de façon "clean".

        Certaines utilisations nécessitent d'avoir un contrôle fin de la mémoire. C++ va jusqu'à permettre de contrôler où on alloue ses objets avec le placement new, si on veut gérer réserver une grande zone de mémoire que l'on va gérer soi même.

Suivre le flux des commentaires

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