IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

GTK+ avec C & C++ Discussion :

Faut-il libérer la mémoire des GtkWidget ?


Sujet :

GTK+ avec C & C++

  1. #1
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut Faut-il libérer la mémoire des GtkWidget ?
    Bonjour,

    Je me rapproche de vous en espérant que vous pourrez m'aider à apporter une réponse CLAIRE, NETTE et PRÉCISE aux deux questions suivantes:

    Faut-il libérer la mémoire des GtkWidget à la sortie du cadre dans lequel ils ont été créé ? ( bien évidemment si l'on en a plus besoin. )

    Et, faut-il aussi le faire à la fermeture du programme ?
    Concrètement faut-il faire ceci?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    MaFonction()
    {
        GtkWidget *pLabel = NULL;
    
        pLabel = gtk_label_new("Ici_mon_texte");
    
             Ici, la suite du code
    
    free(pLabel); // Je libère la mémoire. pLabel = NULL; // Je réinitialise le pointeur à NULL. }

    Les recherches que j'ai pu faire sur le net, ne m'ont pas apporté de solution pérenne.

    Une fois c'est: "Non, le système s'en charge."

    Une autre fois c'est: "Oui, le système s'en charge, mais c'est mieux de le faire quand même... on ne sait jamais."

    Ou bien alors: "Oui, il faut le faire...."

    C'est la net, il y a à boire et à manger...


    Or, si j'ai bien compris le fonctionnement de la mémoire, un GtkWidget ( ici pLabel ) est rangé dans la pile, qui sera automatiquement vidée à la sortie de la fonction et du programme.

    Donc, si c'est ça, free(pLabel) et pLabel = NULL deviennent inutile.

    Oui mais, c'est si j'ai bien compris... et je ne suis sûr de rien.

    D’où ma requête auprès de vous.


    Merci de tout l'éclairage que vous saurez m'apporter.

    Bonne soirée et bon WE.

    Claude

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 291
    Points : 4 941
    Points
    4 941
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    Effectivement, tout GtkWidget affiché fait partie de la pile gérée par la boucle de Gtk+. Donc, lorsqu'on exécute gtk_main_quit(); tous les GtkWigets dans la pile sont détruits. De ce côté là, plus de problème .

    Maintenant, tu peux avoir des widgets que tu crées à un moment donné dans ton application et dont l'utilisation n'est que temporaire; par exemple une fenêtre d'avertissement. Ici est-ce nécessaire de la conserver en mémoire ? Tout va dépendre de la manière dont tu comptes gérer cette fenêtre. Si cette fenêtre apparaît régulièrement il peut être intéressant de la créer une seule fois et de la cacher ensuite. Son apparition sera plus rapide les fois suivantes. Sinon un gtk_widget_destroy (); reste une bonne méthode. Quand on n'en plus besoin, on jette (pas très écolo je te l'accorde ).
    Pour continuer sur cet exemple, tous les GtkWidgets que tu auras pu intégrer à cette fenêtre d'avertissement seront détruits eux aussi lors de la destruction du widget parent.

    Pour finir, il existe un cas où il ne faudra pas détruire les fenêtres mais plutôt les cacher : lorsque tu utilises un fichier Glade pour les créer. Si tu viens à détruire une de ces fenêtres il te faudra recharger le fichier Glade à nouveau pour y accéder. Ce n'est pas très optimisé comme fonctionnement, tu me l'accorderas.

    En espérant avoir éclairé un peu ta lanterne...

  3. #3
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Bonjour,


    Un GRAND MERCI pour ces explications on ne peut plus CLAIRES, NETTES et PRÉCISES.

    Difficile de faire mieux.

    C'est maintenant beaucoup, beaucoup plus claire pour moi.

    Bon WE.

    Claude

  4. #4
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Bonjour,

    le fil a beau être résolu, j'aimerais apporter quelques précisions:

    Faut-il libérer la mémoire des GtkWidget à la sortie du cadre dans lequel ils ont été créé ? ( bien évidemment si l'on en a plus besoin. )
    Oui et non . Normalement il vaut mieux détruire les widgets quand ils ne sont plus utilisés, tu simplement parce que tu libère de la mémoire pour le système. Aucun utilisateur n'aime les applications très gourmande en mémoire. Mais dans certains cas, pour des fenêtres lourdes à construire et affichées fréquemment, ce serait idiot de les détruire pour les recréer plus tard (ça prend du temps, et l'utilisateur n'aime pas attendre et préférera certainement qu'on utilise un peu plus de mémoire pour cela). Tu préféreras juste les masquer à coup de gtk_widget_hide. C'est d'ailleurs pour ça qu'à été créée gtk_widget_hide_on_destroy: tu la connectes au un delete-event de ta fenêtre, qui serai ainsi masquée au lieu d'être détruire quand l'utilisateur clique sur la croix de fermeture.
    Pour résumer, c'est une politique à adopter spécifique à ton application car spécifique aux fenêtres/widgets concernés, d'où le fait que tu aies plusieurs sons de cloche.

    Ensuite, il y a une notion qui n'a pas été abordée ici: le principe des compteurs de référence. Un compteur de référence, c'est juste un nombre qui indique combien de fois un objet est utilisé. C'est intégré dans GObject, et tous les widgets dérivent de GObject. Je te conseille donc de lire cette section de manuel: https://developer.gnome.org/gobject/...ct-memory.html
    En gros, le compteur de référence, c'est un système de jetons. Quand tu utilises un objet, tu prends un référence sur cet objet (avec g_object_ref), et l'object incrémente son compteur pour dire "hé, quelqu'un m'utilise, je suis encore important, ne me détruisez pas !". Quand tu n'utilises plus un objet, tu rends la référence (avec g_object_unref). Ce décrémente le compteur. Et quand le compteur arrive à 0 après un appel à g_object_unref, l'object sais que plus personne ne l'utilise et il s'auto-détruit. Pratique pour éviter fuites de mémoire !

    "Mais quand on crée le widget la première fois, personne ne l'utilise et il devrait s'auto-détruire, non ?" me diras-tu. Bon, là je ne suis pas expert, et je n'ai jamais utilisé les références flotttantes, donc ce que je vais dire est à prendre avec de grosses pincettes. Il me semble que quand tu crées un widget, il arrive généralement avec une référence flottante (weak reference) qui n'est là que pour éviter cela. Au premier appel à g_object_ref, la référence flottante cède sa place à une référence normale, ce qui fait qu'au prochaine g_object_unref, le compteur tombera à 0 et l'objet sera détruit.

    Cela explique donc pourquoi on ne détruit pas tous les widget. Si tu as une fênêtre avec plein de widgets à l'intérieur, détruis la fenêtre et les widgets qu'elle contient seront automatiquement détruits de manière récursive, plus personne ne possédant de référence sur eux.

    Et, faut-il aussi le faire à la fermeture du programme ?
    Le système d'exploitation la rendra à la sortie de l'application, donc tu n'es pas obligé de le faire. Le problème c'est que certains développeurs ne font pas attention au cycle de vie des objets qu'ils utilisent, et s'ils se disent "on libère toujours la mémoire pour moi", ça finit par de grosses fuites ou des applications qui bouffent une tonne de mémoire pour rien.

    Une autre raison est que certains outils de détection de fuite mémoire (genre Valgrind) vont gueuler si tu ne libères pas tout, parce qu'ils n'ont pas moyen de différencier de la mémoire que tu n'as pas désalloué volontairement, de celle que tu n'as pas désalloué involontairement (et qui est donc une fuite mémoire). Du coup si tu veux partir à la recherche de fuites de ressources, c'est plus galère avec un programme qui ne libère pas ses ressources avant de quitter.

    Pour finir, il me semble bon d'apporter une précision: dans ton code, ton widget n'est pas sur la pile, il est sur le tas. C'est ton pointeur vers le widget qui est sur la pile. En sortant de ta fonction, le pointeur pLabel est détruit. Mais pas la zone mémoire vers laquelle il pointait. Le widget en lui même ne sera détruit que si tu le détruis explicitement (g_object_unref, gtk_widget_destroy). Le code ci dessous provoque une fuite:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MaFonction(void)
    {
        GtkWidget *pLabel = gtk_label_new("Ici_mon_texte");
    }
    Le pointeur pLabel est sur la pile, et sera détuit en sortant de la fonction, mais toutes les fonctions _new de gtk créent un objet en faisant des malloc, ce qui alloue de la mémoire sur le tas. Le code ci dessus génère donc une fuite de mémoire: à la sortie de la fonction, tu n'as plus l'adresse du widget, tu ne peux donc plus le détruire, cette mémoire ne pourra être restituée au système qu'à la fermeture de ton programme.

    De plus, pour détruire un dérivé de g_object, tu n'utilises pas free ou g_free, mais g_object_unref ou gtk_widget_destroy. En effet si l'objet en question a des pointeurs vers d'autres zones mémoire allouées, celles ci seraient perdues, générant une fuite. Il faut donc bien faire attention à utiliser les bons couples de fonctions pour allouer/désallouer de la mémoire, car cela peut causer des problèmes. Par exemple en C++, la mémoire alloué par new se détruit avec delete. En C, tu alloues avec malloc, realloc, et tu détruits avec free. Et quand tu utilises une bibliothèque quelconque, c'est pareil. Pour la glib, utilise g_free pour détruire la mémoire, et dans gtk utilise g_object_unref.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  5. #5
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Bonjour,


    J'ai cru bon, suite à la réponse de "gerald3d", qui m'avait semblé claire et évidente, de solder la discussion.
    Mais apparemment, j'ai fait une petite boulette.

    Merci en tout cas de relancer le sujet... et de recréer le doute dans mon esprit.

    Ton explication est claire elle aussi et convaincante, mais alors, que dois-je faire ?

    Ne rien détruire... en me disant que de toute façon le système s'en chargera. ( ce qui dans ce cas, semble faux. )

    Ou bien,

    Détruire les Widgets qui ne sont plus utilisés avec, g_object_unref() et gtk_widget_destroy().

    Je ne suis pas un "pro" mais un amateur qui se veut consciencieux.

    C'est donc plus pour mon éducation qu'autre chose et pour me permettre de faire les choses CORRECTEMENT.

    J'ai eu à utiliser la SDL pour un petit programme de "Labyrinthe" et je ne m'étais pas autant posé la question.
    Si je me souviens bien, la doc est très précise en la matière: Il convient de détruire toutes créations par un "SDL_DestroyQuelqueChose(pXXX).

    Mais je soupçonne ne pas être le seul à me poser ce genre de question et à en attendre ( ou espérer ) une réponse franche et tranchée... Style SDL.


    J'ai je pense et j'espère, bien compris ton premier paragraphe: "oui et non".
    Où il est question d'adopter la bonne méthode en fonction de l'application.

    J'avais déjà entendu parlé de ces "compteurs de références" sans trop bien comprendre...
    C'est maintenant UN PEU plus clair... mais ça reste encore très flou, ne voyant pas trop l'intérêt de ce fameux "compteurs".

    J'ai lu et je relirai avec beaucoup d'attention ton lien sur "la gestion de la mémoire avec GTK", mais c'est en anglais.
    Shakespeare et moi, on est pas trop copain, même si je me fais largement aider par "Google", ça reste pas évident à comprendre.

    Je ne connaissais pas et je le garde sous le coude le: "gtk_widget_hide_on_destroy:"


    Donc, si j'ai bien tout compris et si je résume tes explications.

    Un GtkWidget sera toujours créé sur le tas via malloc.

    Je devrai donc, en sortant de sa fonction créatrice (boucle ou autre), le détruire de la façon suivante.
    g_object_unref(pMonWidget); --> remise à zéro du compteur de référence.
    gtk_widget_destroy(pMonWidget); --> Libération de la mémoire.

    Et éventuellement ( afin de faire les choses correctement ) libérer le pointeur qui va avec et qui lui, a été créé sur la pile et qui de toute façon sera détruit à la fermeture du programme.
    g_free(pMonWidget); ( et non pas free(pMonWidget); )

    Faut-il aussi remettre le pointeur à NULL ? ( pMonWidget = NULL; )



    En attendant d'autres réponses... je remets le fil à "non résolu".

    Donc à suivre.


    Et encore merci pour cette relance.


    Claude

  6. #6
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut Un doute, deux doute, trois doute...
    Salut,

    Merci en tout cas de relancer le sujet... et de recréer le doute dans mon esprit
    "Le doute est le commencement de la sagesse"
    Aristote
    Il convient de détruire toutes créations
    Oui mais c'est fastideux. C'est pour ca que gtk à mis en place un système de libération de la mémoire automatique. De ce fait gtk_container_add() n’incrémente pas le widget enfant.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    GtkWidget *label;
    int swap_container_vertical_to_horizontal(int argc, char *argv[]) {
      gtk_init();
     
      label = gtk_label_new("ref");
     
      {
        GtkWidget *vcontainer = gtk_vbox_new();
        GtkWidget *hcontainer = gtk_hbox_new();
     
        gtk_container_add(GTK_CONTAINER(vcontainer), label);// _add n'a pas incrementé ref
        g_print("ref counter: %d\n", G_OBJECT(label)->ref);
        g_object_ref(label); //incrémenter manuellement label
     
        gtk_container_remove(GTK_CONTAINER(vcontainer), label);// _remove a décrementé ref
        g_print("ref counter: %d\n", G_OBJECT(label)->ref);
     
        gtk_container_add(GTK_CONTAINER(hcontainer), label);
        g_print("ref counter: %d\n", G_OBJECT(label)->ref);
        g_object_ref(label); //incrémenter manuellement label
     
        g_object_unref(hcontainer); // destroy hcontainer si plus utilisé ailleurs
        g_object_unref(hcontainer); // destroy vcontainer si plus utilisé ailleurs et ses enfants
      }
     
      {
        // c'est repartie pour un tour de composition
        g_print("ref counter: %d\n", G_OBJECT(label)->ref);
      }
     
      g_object_unref(label); // destroy label de mon application manuellement
     
      return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    MaFonction()
    {
        GtkWidget *pLabel = gtk_label_new("Ici_mon_texte");
        GtkWidget *pBox = gtk_vbox_new();
     
        gtk_box_pack_start(GTK_BOX(pBox), pLabel);
     
       g_object_unref(pBox);        // Je libère tous les widget et par conséquence la mémoire si pBox et/ou pLabel ne sont pas utilisé ailleurs.
    }
    Je crois savoir que la séquence de destruction d'un widget est celle-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      gtk_container_remove()
        g_object_unref()
          gtk_widget_destroy()
            gtk_widget_finalize()
    Donc éviter gtk_widget_destroy() au bénéfice de g_object_unref()

    il est question d'adopter la bonne méthode en fonction de l'application.
    Non, du contexte. Tu peux par exemple laisser Gtk détruire ta fenêtre principale via le systeme gtk_main_quit(). Et utiliser le système g_object_ref/unref() pour géré la destruction(en cascade) d'une composition de widget. Utiliser gtk_widget_destroy() c'est de la haute voltige.

  7. #7
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Citation Envoyé par Gaulouis Voir le message
    Salut,
    Oui mais c'est fastideux. C'est pour ca que gtk à mis en place un système de libération de la mémoire automatique. De ce fait gtk_container_add() n’incrémente pas le widget enfant.
    C'est un peu plus compliqué que ça. Si un widget fraîchement créé possède un compteur de références initialisé à 1, pas 0, c'est parce que c'est plus pratique à utiliser en C.
    Mais c'est une référence spéciale, une référence flottante.

    En gros, c'est ce qui permet d'écrire (exemple tiré de la document de GInitiallyUnowned):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    // GTK officiel, avec référence flottante
    container = create_container ();
    container_add_child (container, create_child());
    Plutôt que le fastidieux (et plus à même de générer des erreurs de la part du développeur, comme le g_object_unref final)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    // GTK imaginaire sans système de référence flottante, comme s'il avait été codé pour embêter les développeurs C
    Child *child;
    container = create_container ();
    child = create_child ();
    container_add_child (container, child);
    g_object_unref (child);
    Selon la même documentation officielle:
    If container_add_child() calls g_object_ref_sink() on the passed-in child, no reference of the newly created child is leaked. Without floating references, container_add_child() can only g_object_ref() the new child.
    Donc si tu appelles ton gtk_container_add sur un objet qui a déjà été ajouté puis retiré d'un conteneur, tu auras bien le compteur qui sera incrémenté, parce que la référence n'est plus flottante. C'est parce que g_object_ref_sink() fait "couler" la référence flottante (en gros il la transforme juste en référence non-flottante, sans incrémenter le compteur), et si elle n'est pas flottante, il incrémente le compteur.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  8. #8
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut
    Donc pour reprendre l'exemple de Claude, je dois écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    MaFonction()
    {
        GtkWidget *pLabel = NULL;
     
        pLabel = gtk_label_new("Ici_mon_texte");
     
        g_object_ref_sink(pLabel);
        g_object_unref(pLabel);
        pLabel = NULL;
    }
    Donc si tu appelles ton gtk_container_add sur un objet qui a déjà été ajouté puis retiré d'un conteneur, tu auras bien le compteur qui sera incrémenté,
    Pourtant mon child semble avoir été détruit après le gtk_container_remove() (ce que je peu éviter avec un g_object_ref(child) avant le remove)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <gtk/gtk.h>
     
    int
    main (int argc, char *argv[])
    {
        GtkWidget *child;
        GtkWidget *container;
     
        gtk_init (&argc, &argv);
     
        child = gtk_label_new("...");
        container = gtk_vbox_new(FALSE, 0);
     
        gtk_container_add(GTK_CONTAINER(container), child);
        gtk_container_remove(GTK_CONTAINER(container), child);
     
        g_print("%d\n", G_OBJECT(container)->ref_count);// 1
        g_print("%s\n", g_type_name_from_instance(child));// <NULL-class>
     
        return 0;
    }
    Je ne vois pas ce qu'apporte une référence flottante

    PS: C'est con que Claude pose ce genre de question, par ce qu'avant j'avais plutôt une belle image de Gtk :°)

  9. #9
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Bonjour,


    Ah, super, le débat semble relancé.


    En réponse à Gaulois:

    C'est pour ça que gtk à mis en place un système de libération de la mémoire automatique.
    C'est le mot automatique qui me gène le plus. Car justement, il n'y a rien d'automatique.
    Les widgets sont stockés sur le TAS et il revient au programmeur de faire le nécessaire pour nettoyer correctement la mémoire afin d'éviter entre autres ce qu'on appelle des "fuites...".
    Ce n'est pas du tout automatique. Contrairement aux variables "classiques" qui elles sont stockées sur la PILE.

    De ce fait gtk_container_add() n’incrémente pas le widget enfant.
    Si, bien sûr, mais pas dans ton exemple.
    Si la référence est flottante il l'a transforme en définitive.
    Si elle est définitive, il l'incrémente.


    Je pense que ton premier programme, tel qu'il est conçu, risque de générer des fuites mémoire à cause des widgets qui n'ont pas été détruits correctement.


    Voila ce que j'obtiens dans la console, lorsque je lance ton programme.
    ref counter: 1
    ref counter: 1
    ref counter: 2

    (ESSAIS.exe:3168): Gtk-WARNING **: A floating object was finalized. This means that someone
    called g_object_unref() on an object that had only a floating
    reference; the initial floating reference is not owned by anyone
    and must be removed with g_object_ref_sink().

    (ESSAIS.exe:3168): Gtk-WARNING **: A floating object was finalized. This means that someone
    called g_object_unref() on an object that had only a floating
    reference; the initial floating reference is not owned by anyone
    and must be removed with g_object_ref_sink().
    ref counter: 2

    Les deux messages d'erreurs concernent le g_object_unref() sur des widgets ayant des références flottantes.
    Et à la toute fin du programme, label sera toujours présent avec un compteur de références égal à 1... d'où risque de problèmes.


    Et comme dit liberforce: "C'est un peu plus compliqué que ça."
    Personnellement, je dirai: beaucoup plus compliqué que ça... Et c'est le moins que l'on puisse dire.

    Bref, je m'explique. (enfin, je vais essayer...)

    En fait dans ton exemple, label est un widget qui descend de GInitiallyUnowned.
    A sa création, le programme lui alloue donc une référence flottante égale à +1. (flottante et non initiale (ou réelle).
    Ce widget descend de GInitiallyUnowned, c'est à dire qu'il n'a pas encore d'utilisateur (ou de propriétaire).
    Et dans ce cas, là où tu l'a positionné (après le passage dans le container) ref_count va afficher +1.
    Pas besoin alors d'utiliser g_object_ref(label) qui ne fait que rajouter +1, ce qui nous donnera un total de +2.

    Lorsque ce widget sera associé à un contenaire, alors à ce moment là et à ce moment là seulement, sa référence flottante sera transformée en référence réelle (ou initiale).
    Pour détruire ce widget, une fois qu'il sera passé dans le container, il suffira alors d'appeller gtk_widget_destroy(label) et non g_object_unref() afin d'éviter un plantage.
    label est un widget qui descend de GInitiallyUnowned et non de GObject.

    gtk_widget_destroy() appelle (implicitement) g_object_unref() pour décrémenter le compteur de label et si ce compteur de références est égal à 0, alors là, le widget sera correctement détruit. Sinon il perdurera.

    Dans ton exemple, il aurait été préférable d'appeler g_object_ref_sink(G_OBJECT(label)) (plutôt que g_object_ref()) qui est là justement pour transformer une référence flottante en référence réelle ou initiale.

    Il convient je pense, de bien faire le distinguo entre ce qui descend de GObject et ce qui descend de GInitiallyUnowned.

    Dans ton deuxième exemple, pLabel et pBox sont des pointeurs sur widgets descendant là aussi de GInitiallyUnowned.
    pLabel aura une référence réelle après son passage dans le contenaire.
    pBox aura toujours une référence flottante, vu qu'il n'a pas encore été intégré à la fenêtre principale. (ou un autre contenaire.)
    Si tu le détruits maintenant, tu ne feras rien que de créer une fuite mémoire.
    Et pour libérer correctement pBox il faudra avant tout lui créer un référence réelle avec g_object_ref_sink(G_OBJECT(pBox)) (ou l'intégrer dans un autre contenaire) puis après, appeler g_object_unref(G_OBJECT(pBox)) dans le premier cas ou gtk_widget_destroy(pBox) dans le second cas.

    Si la référence flottante est transformée en référence réelle par le passage du widget dans un contenaire, il faudra alors utiliser gtk_widget_destroy(pWidget) pour le détruire. Dans ce cas, l'utilisation d'un g_object_unref(pWidget) entraînera un plantage.
    Si la référence flottante est transformée en référence réelle en utilisant g_object_sink(pWidget) il faudra alor utiliser g_object_unref(pWidget). Dans ce cas, l'utilisation de gtk_widget_destroy(pWidget) n'aura aucun effet.

    Je crois savoir que la séquence de destruction d'un widget est celle-ci :
    ??? une séquence ? pas à ma connaissance...
    Voila ce qu'en dit la doc:

    gtk_container_remove();
    Enlève le widget du conteneur... il est généralement plus efficace de simplement le détruire directement en utilisant gtk_widget_destroy().

    g_object_unref();
    Il diminue le nombre de références de l'objet . Et dans le cas d'un GObject, lorsque son compteur de références tombe à 0, la mémoire de l'objet est libérée.

    gtk_widget_destroy();
    Demande simplement la destruction du widget.
    gtk_widget_destroy() ne fait rien de son propre chef, il va simplement demander l'autorisation au programme de détruire correctement le widget. Il appelle implicitement g_object_unref() et si la référence est égal à 0 alors le widget sera correctement supprimé.

    gtk_widget_finalize() ???? pas trouvé de trace sur la doc.

    Donc éviter gtk_widget_destroy() au bénéfice de g_object_unref().
    NON, se sont deux fonctions différentes pour des utilisations différentes.

    gtk_widget_destroy() est à utiliser sur les widgets descendants de GInitiallyUnowned.
    g_object_unref() est à utiliser sur les widgets descendants de GObject.


    Et utiliser le système g_object_ref/unref() pour géré la destruction (en cascade) d'une composition de widgets.
    Lorsqu'une fenêtre "toplevel" est détruite, elle entraine avec elle, la destruction de ses enfants... pas besoin de destruction en cascade.


    Utiliser gtk_widget_destroy() c'est de la haute voltige.
    NON, si on applique correctement les règles, c'est hyper simple.
    Enfin, quand je dis " hyper "... c'est plus une façon de parler.



    Dans ton deuxième post:

    Le premier code:
    Oui, ça fonctionne correctement. Voilà la copie de l'écran console que j'obtiens avec ton code.

    Référence: 1

    (ESSAIS 2.exe:5844): GLib-GObject-WARNING **: invalid unclassed pointer in cast to `GObject'

    Référence: 0

    <NULL-class>

    Le WARNING est normal, puisque dans ce cas, on appelle un pointeur qui n'existe plus.


    Pourtant mon child semble avoir été détruit après le gtk_container_remove() (ce que je peux éviter avec un g_object_ref(child) avant le remove)
    Oui c'est vrai, mais je ne vois pas l'intérêt de remove l'enfant pour le recréer immédiatement après.
    Sachant que gtk_container_remove(); Enlève le widget du conteneur...sans toucher à ce dernier.


    Je ne vois pas ce qu'apporte une référence flottante.
    Un contenaire incrémente automatiquement le compteur de références de l'enfant.
    Supposons qu'à la création de l'enfant, celui-ci se voit attribuer une référence réelle de +1.
    Lorsqu'il passerait dans le contenaire sa référence serait alors égale à +2.
    D'où la nécessite de créer au départ une référence flottante.

    Une référence qui attend le passage dans le conteneur.
    Une référence qui en fait n'appartient à personne...
    d'où GInitiallyUnowned qui signifie: G Initialement non propriétaire.


    Et pour terminer:

    PS: C'est con que Claude pose ce genre de question, par ce qu'avant j'avais plutôt une belle image de Gtk :°)
    désolé...


    J'essaie, (comme dit dans un autre post) de parfaire mes connaissances dans la gestion de la mémoire en GTK+ et je travaille dessus depuis quelques temps.

    Et c'est loin d'être simple et évident.

    J'ai trouvé une note inintéressante d'un professeur d’université de New York qui traite de ce sujet, dont l'adresse est ICI. (si ça t'intéresse ?)


    Ce qui est dit ci-dessus est un résumé de ce que je crois avoir compris à la lecture de divers documents traitant du sujet.

    Mais, je ne suis pas un professionnel et j'ai pu mal comprendre (ou traduire...) certains passages.

    Aussi, je remercie tout ceux qui pourraient relever dans mes propos de grosses erreurs (ou des petites) d'avoir la gentillesse de me le signaler en m'expliquant le pourquoi du comment.


    Sur ce, bon Week-end à tous.


    Claude

  10. #10
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Citation Envoyé par Claude URBAN Voir le message
    Lorsque ce widget sera associé à un contenaire, alors à ce moment là et à ce moment là seulement, sa référence flottante sera transformée en référence réelle (ou initiale).
    Pour détruire ce widget, une fois qu'il sera passé dans le container, il suffira alors d'appeller gtk_widget_destroy(label) et non g_object_unref() afin d'éviter un plantage.
    label est un widget qui descend de GInitiallyUnowned et non de GObject.
    Non, ce n'est pas à cause de ça. label descend bien de GObject. Pas directement, mais il hérite bien de GObject, et tu peux faire avec lui tout ce que tu ferais avec un GObject.
    Si on ne peut pas appeler g_object_unref directement sur le label, c'est parce que le conteneur ne serait pas prévenu que le widget a été détruit. gtk_widget_destroy va lui couper toute relation avec le conteneur, et si cela a pour effet que plus personne n'a de de référence sur le label, le label sera détruit...

    Citation Envoyé par Claude URBAN Voir le message
    gtk_widget_destroy() est à utiliser sur les widgets descendants de GInitiallyUnowned.
    g_object_unref() est à utiliser sur les widgets descendants de GObject.
    Le reste de tes conclusions est assez juste, mais cette partie là est fausse.
    Bon, je vais essayer de trouver de temps pour programmer quelques exemples, parce que tout ça a encore l'air confus.
    Je vais sans doute apprendre des choses aussi :p
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  11. #11
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Voilà un exemple/exercice.

    étape 1: compilez, exécutez, lisez et regardez le code.
    étape 2: remplacez le g_object_unref de la ligne 71 par le gtk_widget_destroy de la ligne 72, puis compilez, exécutez, lisez.

    Que pouvez vous en déduire ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    #include <gtk/gtk.h>
     
    void print_ref_count(GtkWidget *widget, const char *name)
    {
    	g_debug("'%s' a %d reference(s)%s",
    			name,
    			G_OBJECT(widget)->ref_count,
    			g_object_is_floating(widget) ? " flottante" : "");
    }
     
    int main (int argc, char *argv[])
    {
    	GtkWidget *vbox;
    	GtkWidget *label;
    	GtkWidget *button;
     
    	gtk_init (&argc, &argv);
     
    	// Avec ça vous aurez les messages de debug
    	g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
     
    	g_debug("Création de label et vbox");
    	label = gtk_label_new("...");
    	vbox = gtk_vbox_new(FALSE, 0);
    	print_ref_count(label, "label");
    	print_ref_count(vbox, "vbox");
     
    	g_debug("Ajout du label dans la vbox: la référence du label devient réelle et plus flottante");
    	gtk_container_add(GTK_CONTAINER(vbox), label);
    	print_ref_count(label, "label");
    	print_ref_count(vbox, "vbox");
     
    	g_debug("Avant de retirer label de la vbox, on va lui ajouter une référence pour éviter sa destruction");
    	g_object_ref(label);
    	print_ref_count(label, "label");
     
    	g_debug("Retrait de label de la vbox: label existe toujours puisqu'on possède une référence sur lui");
    	gtk_container_remove(GTK_CONTAINER(vbox), label);
    	print_ref_count(label, "label");
    	print_ref_count(vbox, "vbox");
     et regardez le code
    	g_debug("Ajout une nouvelle fois du label dans la vbox");
    	gtk_container_add(GTK_CONTAINER(vbox), label);
    	print_ref_count(label, "label");
    	print_ref_count(vbox, "vbox");
     
    	g_debug("On retire la référence sur label qu'on a ajouté manuellement quand on a appelé g_object_ref");
    	g_object_unref(label);
    	print_ref_count(label, "label");
     
    	g_debug("Retrait de label de la vbox: label va être détruit car la vbox était la dernière à posséder une référence sur le label");
    	gtk_container_remove(GTK_CONTAINER(vbox), label);
    	print_ref_count(label, "label"); // Attention, ici on accède à de la mémoire qui n'est plus allouée !!!!
    	print_ref_count(vbox, "vbox");
     
    	g_debug("Création de button");
    	button = gtk_button_new();
    	print_ref_count(button, "button");
     
    	g_debug("Ajout de button dans la vbox: la référence de button devient réelle et plus flottante");
    	gtk_container_add(GTK_CONTAINER(vbox), button);
    	print_ref_count(button, "button");
    	print_ref_count(vbox, "vbox");
     
    	g_debug("On 'coule' (transforme la référence flottante en référence réelle) la référence de vbox.\n"
    			"Autrement on aurait un warning à la destruction, car c'est bizarre de détruire quelque chose qu'on a pas utilisé.");
    	g_object_ref_sink(vbox);
    	print_ref_count(vbox, "vbox");
     
    	g_debug("On tente de détruire la vbox: que deviennent vbox et button ?");
    	g_object_unref(vbox);
    	// gtk_widget_destroy(vbox);
    	print_ref_count(button, "button");
    	print_ref_count(vbox, "vbox");
     
    	return 0;
    }
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  12. #12
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Merci liberforce pour tes commentaires.

    Prenons les dans l'ordre. D'abord le premier, celui de 15h 27.

    J'approfondirai le second après. (c'est plus compliqué)

    Je veux bien, mais comment expliques-tu que le code ci-dessous PLANTE:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     
    GtkWidget *pLabel = NULL;
     
    GtkWidget *vbox = NULL;
     
    pLabel = gtk_label_new("Ici_mon_texte");
     
    vbox = gtk_box_new (TRUE, 6);
     
    gtk_box_pack_start (GTK_BOX (vbox), pLabel, TRUE, TRUE, 0);
     
    g_object_unref(G_OBJECT(pLabel));
    Voici les messages d'erreur: avec un plantage complet.
    (Process terminated with status -1073741819)

    Référence: 1

    (ESSAIS 2.exe:4284): Gtk-CRITICAL **: gtk_widget_get_realized: assertion `GTK_IS_WIDGET (widget)' failed

    (ESSAIS 2.exe:4284): GLib-GObject-WARNING **: instance with invalid (NULL) class pointer

    (ESSAIS 2.exe:4284): GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion G_TYPE_CHECK_INSTANCE (instance)' failed

    (ESSAIS 2.exe:4284): GLib-GObject-WARNING **: instance with invalid (NULL) class pointer

    (ESSAIS 2.exe:4284): GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed

    (ESSAIS 2.exe:4284): GLib-GObject-WARNING **: instance with invalid (NULL) class pointer

    (ESSAIS 2.exe:4284): GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed


    Alors que celui-ci, fonctionne correctement.
    Je remplace simplement g_object_unref(G_OBJECT(pLabel)); par gtk_widget_destroy(pLabel);

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
     
    GtkWidget *pLabel = NULL;
     
    pLabel = gtk_label_new("Ici_mon_texte");
     
    printf("\n\n %s\n\n", g_type_name_from_instance((GTypeInstance *) pLabel));  // GtkLabel
     
        //g_object_ref_sink(G_OBJECT(pLabel));
     
    gtk_box_pack_start (GTK_BOX (vbox), pLabel, TRUE, TRUE, 0);
     
    printf("\n Référence: %d\n\n", G_OBJECT(pLabel)->ref_count); // Référence: 1
     
    gtk_widget_destroy(pLabel);
     
        //g_object_unref(G_OBJECT(pLabel));
     
    printf("\n Référence: %d\n\n", G_OBJECT(pLabel)->ref_count); // Référence: 0
     
    printf(" %s\n\n", g_type_name_from_instance((GTypeInstance *) pLabel));  //<NULL-class>
     
    pLabel = NULL;
     
    g_print(" %s\n", g_type_name_from_instance((GTypeInstance *) pLabel));   //<NULL-instance>
    GtkLabel

    Référence: 1

    (ESSAIS 2.exe:3236): GLib-GObject-WARNING **: invalid unclassed pointer in cast to `GObject'

    Référence: 0

    <NULL-class>

    <NULL-instance>


    ... c'est parce que le conteneur ne serait pas prévenu que le widget a été détruit...
    Pourtant, ci le label est détruit, c'est que sa référence est retombée à 0.
    Donc tout le monde peut le savoir... y compris bien évidement le conteneur.

    Là, j'ai un GROS doute.


    C'est l'expérience sur le tas qui m'a amené aux conclusions suivantes.

    Si la référence flottante est transformée en référence réelle par le passage du widget dans un contenaire, il faudra alors utiliser gtk_widget_destroy(pWidget) pour le détruire. Dans ce cas, l'utilisation d'un g_object_unref(pWidget) entraînera un plantage.

    Si la référence flottante est transformée en référence réelle en utilisant g_object_sink(pWidget) il faudra alors utiliser g_object_unref(pWidget). Dans ce cas, l'utilisation de gtk_widget_destroy(pWidget) n'aura aucun effet.


    Bon maintenant je vais attaquer le deuxième post.

  13. #13
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut
    que deviennent vbox et button ?
    Ils deviennent des non-objects par ce que g_object_unref à causé leurs perte(A la destruction du container l'enfant est détruit).

    Mais est-ce pour autant que la mémoire est libéré ?

    J'ai fait un exemple où je crée un widget, je le libère, puis je crée un nouveau widget qui devrai avoir la même adresse que mon premiers. (J'ai essayé avec malloc/free; avec g_new et g_free; et j'obtiens bien ce comportement) Mais ce n'est pas le cas avec g_object_unref.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <gtk/gtk.h>
     
    int
    main (int argc, char *argv[])
    {
        GtkWidget *pLabel = NULL;
     
        pLabel = gtk_label_new("Ici_mon_texte");
     
        g_print("pLabel %p\n", pLabel);// 0x00030
     
        g_object_ref_sink(pLabel);// sinon unref me crie dessus
        g_object_unref(pLabel);
        if ( ! GTK_IS_WIDGET(pLabel) ) g_print("pLabel n'est plus un widget\n");
     
        // par curiosité
        //g_idle_add(gtk_main_quit, NULL);
        //gtk_main();
     
        pLabel = gtk_label_new("Ici_mon_texte");
        g_print("Pourquoi pLabel = %p\n", pLabel);
     
        return 0;
    }
    J'obtiens l'affichage "Pourquoi mLabel = 0x00100" alors que je m'attendais à obtenir 0x00030

    Du coup je doute que la mémoire soit libéré. Par contre après unref le pointer pLabel est bien inutilisable.

    Donc je reviens sur la question de Claude qui était: comment libérer la mémoire ?

    Edit: j'ai repris l'exemple en bouclant la création/destruction et okay la mémoire est bien libéré.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include <gtk/gtk.h>
     
    int
    main (int argc, char *argv[])
    {
        GtkWidget *pLabel = NULL;
        gchar *palette[7] = {"#B22222", "#A52A2A", "#DAA520", "#006400", "#40E0D0", "#0000CD", "#800080"};
     
        g_print("[tr]\n");
        int i;
        int x = 0;
        int y = 0;
        for (i=0; i<98; i++) {
            /// Création
            pLabel = gtk_label_new("Ici_mon_texte");
            /// Fin Création
     
            // convenience
            y = i%7;
            if (y==0) {
                g_print("[/tr]\n");
                g_print("[tr]\n");
                x++;
            }
            if (x%2) {
                y = 6 - y;
            }
            g_print("\t[td][COLOR=\"%s\"]%p[/COLOR][/td]\n", palette[y], pLabel);// 0x00030
            // end convenience
     
            /// Detruction
            g_object_ref_sink(pLabel);
            g_object_unref(pLabel);
            /// Fin Detruction
        }
        g_print("[/tr]\n");
     
        return 0;
    }
    J'obtien quelques chose comme ce-ci: (peut être du à g_slice ? )
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030
    0x1fce030 0x1fce100 0x1fce1d0 0x1fce2a0 0x1fce370 0x1fce440 0x1fce510
    0x1fce510 0x1fce440 0x1fce370 0x1fce2a0 0x1fce1d0 0x1fce100 0x1fce030

    Par contre je ne sais pas comme réutiliser de la mémoire libéré par gtk_widget_destroy()

  14. #14
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut

    Dans cet exemple:


    La référence réelle de button a été créée à partir du contenaire vbox.
    La référence réelle de vbox a été créée avec g_object_ref_sink(vbox).

    A la destruction d'un contenaire, l'enfant perd une référence.
    Si cette référence est égale à +1, alors elle devient égale à 0 et l'enfant est automatiquement détruit.

    Si on utilise g_object_unref(vbox), les deux références (button et vbox) seront décrémentées et égales à 0.
    Et les widgets seront eux détruits.

    MAIS, (je pense que) la référence de button a été détruite PARCE QUE le contenaire a été détruit, pas par l'utilisation de g_object_unref(vbox).


    Maintenant, si on utilise gtk_widget_destroy(vbox), button perd sa référence et le widget button est détruit.
    destroy(vbox) appelle les références qui sont attachées au contenaire (vbox) ( les enfants) et les détruits.
    MAIS, vbox conserve sa référence et ne sera pas détruit parce que sa référence réelle a été créée avec g_object_ref_sink(vbox).
    destroy() ne détruit pas les références créées avec g_object_ref_sink().


    Je reviens sur ce que j'ai dit plus haut et ce que l'on vient de voir semble le confirmer:

    Si la référence flottante est transformée en référence réelle par le passage du widget dans un contenaire, il faudra alors utiliser gtk_widget_destroy(pWidget) pour le détruire. Dans ce cas, l'utilisation d'un g_object_unref(pWidget) entraînera un plantage.

    Si la référence flottante est transformée en référence réelle en utilisant g_object_sink(pWidget) il faudra alors utiliser g_object_unref(pWidget). Dans ce cas, l'utilisation de gtk_widget_destroy(pWidget) n'aura aucun effet.


    Pour faire une première réponse à Gaulois.
    C'est le but même des références, tant qu'elles existent, le widget existe.
    Lorsque le compteur de référence d'un widget atteint 0 (il n'y a plus de propriétaire ou d'utilisateur), alors le widget est AUTOMATIQUEMENT détruit... donc désalloué.
    bye bye widget...

    Sur ce et en attendant vos commentaires... bonne soirée.

    Claude.

  15. #15
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut Petite traduction de la FAQ Gtk+2
    Faut-il libérer la mémoire des GtkWidget ?
    La FAQ Gtk+ nous donne la réponse au chapitre 1.5.


    Question : Pourquoi mon programme a une fuit de mémoire, si je détruis un widget immédiatement après l'avoir créé ?

    • Si GtkFoo n'est pas une fenêtre toplevel, alors foo = gtk_foo_new (); gtk_widget_destroy (foo); provoquera une fuite de mémoire par ce que rien n'a endosser la référence flottante.


    • Si vous utilisez un widget et que vous ne l'ajoutez pas immédiatement à un conteneur, alors vous souhaiterez probablement le compteur de référence standard (pas flottante). Pour obtenir cela, vous devez acquérir une référence au widget ("ref" dans le langage GTK +) et abandonner la référence flottante ( "sink" dans le langage GTK +) après sa création.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      foo = gtk_foo_new ();
      g_object_ref (foo); 
      gtk_object_sink (GTK_OBJECT (foo));
    • Lorsque vous voulez vous débarrasser du widget, vous devez appeler gtk_widget_destroy() pour briser toutes les connexions externes au widget avant d'abandonner votre référence:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      gtk_widget_destroy    (Foo);  
      g_object_unref    (Foo);
    • Lorsque vous ajoutez immédiatement un widget à un conteneur, il prend soin d'assumer la référence initiale flottante et vous n'avez pas du tout à vous soucier du compteur de référence... il suffit d' appeler gtk_widget_destroy() pour se débarrasser du widget.

    Pour supprimer pLabel, on peut donc écrire en toute sécurité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    GtkWidget *pLabel = gtk_label_new("Ici_mon_texte");
    gtk_box_pack_start (GTK_BOX (vbox), pLabel, TRUE, TRUE, 0);
    gtk_widget_destroy(pLabel);
    Mais ça, ça marche aussi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    gtk_container_remove (GTK_CONTAINER (vbox), pLabel);
    PS: Je n'ai pas trouvez de réponse, mais peut être que les référence flottante sont utilisé par le ramasse miettes(garbage collector)

  16. #16
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Bonjour,


    Faut-il libérer la mémoire en GTK+ et si oui, comment faire ?
    Compte tenu de tout ce qui a été dit précédemment et des différents liens que les participants ont partagés, je pense que l'on pourrait donner la réponse suivante:

    Au démarrage, l’OS alloue une zone mémoire nécessaire et suffisante pour que le programme puisse s’exécuter correctement.
    À la fermeture dudit programme, l’OS récupère sa mémoire, non sans l’avoir auparavant nettoyée.

    Donc, sauf à avoir spécifiquement besoin de se débarrasser de certains widgets devenus inutiles et/ou avoir besoin de récupérer de la place mémoire, il est INUTILE de faire quoi que ce soit en cours de route.

    Et pour les puristes, attendre patiemment la fermeture du programme, à ce moment-là et afin de faire les choses proprement et professionnellement, vider la mémoire avant d’arrêter la boucle événementielle.
    Tous les enfants dépendants de la fenêtre toplevel seront détruits en même temps.
    Bien que, sachant qu’à LA FERMETURE du programme, l’OS récupère la mémoire qu’il a allouée et la nettoie de toutes ses scories… l’on peut se demander si à ce niveau, tout ceci est bien utile ??
    Mais en tout cas, ça ne fait de mal à personne...


    Maintenant, si vous avez besoin de récupérer de la mémoire, voici comment il conviendrait de faire pour détruire correctement un widget.

    Rappel: Pour qu’un widget puisse être correctement détruit, IL EST IMPÉRATIF que son compteur de référence soit égal à 0.

    1/ Le widget descend de GObject.
    Décrémentation du compteur de références :
    g_object_unref (pWidget);
    pWidget = NULL;

    Lorsque le compteur de référence aura atteint 0, alors le widget sera automatiquement et correctement détruit.

    2/ Le widget descend de GInitiallyUnowned.
    a/ Le widget a été intégré à un conteneur.
    gtk_widget_destroy (pWidget) ;
    pWidget = NULL ;

    b/ Le widget n’a pas été intégré à un conteneur.
    Il convient alors de transformer sa référence flottante en référence réelle.
    g_object_ref_sink (pWidget) ;

    Puis de la détruire.
    g_object_unref (pWidget) ;
    pWidget = NULL ;

    L’on peut vérifier en cours de route pendant la conception du programme où en est le compteur de références de la façon suivante :
    printf (" Référence de pWidget : %d\n", G_OBJECT (pWidget)->ref_count) ;

    Et éventuellement pour voir si le widget est toujours en vie :
    printf ("Nom du widget: %s\n", g_type_name_from_instance((GTypeInstance *) pWidget)) ;

    Si le widget existe toujours, cela affichera le nom du widget : GtkNom_Widget.
    Si le widget est détruit, cela affichera : <NULL_class>

    Mais ATTENTION, les widgets ne sont pas les seules variables créées par GTK+.
    Il peut aussi exister dans le programme d’autres variables qui de par leur nature auraient été stockées dans le TAS.
    Il ne faut pas penser que ce qui est décrit ci-dessus va tout solutionner.
    Je fais particulièrement allusion à certaines variables créées avec la GLib, et plus spécialement : g_strdup(), g_strnfill(), ou g_strdup_printf().

    La règle générale est que, si la documentation indique que la fonction retourne une nouvelle chaîne allouée, la chaîne retournée doit être libérée avec g_free().
    Si la documentation ne spécifie rien, alors il ne faut pas appeler g_free() sur la chaîne, sauf si vous souhaitez un plantage du programme.
    Et il en est de même pour les structures GdkPixbuf.

    Comme quoi, il est IMPÉRATIF de consulter la documentation AVANT d’utiliser une fonction que vous ne connaissez pas ou peu, afin d’éviter de faire certaines erreurs.


    Et pour répondre à la dernière question de Gaulois.

    PS : … mais peut-être que les références flottantes sont utilisées par le ramasse-miettes (garbage collector)

    Je crois savoir, qu’il n’existe pas de ramasse-miettes lorsque GTK+ est utilisé en C. (Python, C++ et autres, je ne sais pas.)

    Qu’en pensez-vous ??

    Sur ce, bonne journée à tous.

    Claude.

  17. #17
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Bon, vous êtes durs avec moi, là...
    Perso je n'ai pas le temps pour lire/tester autant de choses, donc je pense que ce sera ma dernière intervention sur ces fils sur la gestion de la mémoire.

    Claude, pour tes exemples de code, mets un code complet avec un main, dans les bouts de code incomplets, on ne sait pas trop dans quel contexte ça s'exécute...
    Ensuite, si tu te poses une question une question très précise sur un fonctionnement, la meilleure réponse à tes questions se trouve dans le code. C'est libre et ouvert, c'est fait pour ça.

    Citation Envoyé par Claude URBAN Voir le message
    Je veux bien, mais comment expliques-tu que le code ci-dessous PLANTE:
    Alors que celui-ci, fonctionne correctement.
    Je remplace simplement g_object_unref(G_OBJECT(pLabel)); par gtk_widget_destroy(pLabel);
    Ces exemples ont leurs limites,

    Citation Envoyé par Claude URBAN Voir le message
    Pourtant, ci le label est détruit, c'est que sa référence est retombée à 0.
    Donc tout le monde peut le savoir... y compris bien évidement le conteneur.
    Le conteneur ne peut pas tester si l'enfant existe encore en accédant à con compteur de référence, tout simplement parce que si l'enfant n'existe pas, cela causerait un accès à de la mémoire non-allouée, et qui peut potentiellement contenir n'importe quoi.

    Citation Envoyé par Claude URBAN Voir le message

    C'est l'expérience sur le tas qui m'a amené aux conclusions suivantes.

    Si la référence flottante est transformée en référence réelle par le passage du widget dans un contenaire, il faudra alors utiliser gtk_widget_destroy(pWidget) pour le détruire. Dans ce cas, l'utilisation d'un g_object_unref(pWidget) entraînera un plantage.

    Si la référence flottante est transformée en référence réelle en utilisant g_object_sink(pWidget) il faudra alors utiliser g_object_unref(pWidget). Dans ce cas, l'utilisation de gtk_widget_destroy(pWidget) n'aura aucun effet.
    gtk_widget_destroy fait des choses g_object_unref ne fait pas (pour une bonne et simple raison: gtk_widget_destroy appelle g_object_run_dispose qui lui même appelle g_object_unref pour toi. Il s'occupe de couper les relations avec les widgets parents et enfants.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  18. #18
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Citation Envoyé par Gaulouis Voir le message
    J'ai fait un exemple où je crée un widget, je le libère, puis je crée un nouveau widget qui devrai avoir la même adresse que mon premiers. (J'ai essayé avec malloc/free; avec g_new et g_free; et j'obtiens bien ce comportement) Mais ce n'est pas le cas avec g_object_unref.
    Les allocateurs ne te donnent aucune garantie de te redonner la même adresse si tu détruits un objet puis le recrées. Tu ne peux absolument pas te fier à ça.
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

  19. #19
    Membre averti
    Avatar de Claude URBAN
    Homme Profil pro
    Prendre le temps de vivre. . .
    Inscrit en
    Mai 2006
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Prendre le temps de vivre. . .

    Informations forums :
    Inscription : Mai 2006
    Messages : 274
    Points : 327
    Points
    327
    Par défaut
    Bonjour liberforce,


    ... ... Alors là, pas tout compris.

    Mais bon.

    ! Désolé pour le dérangement...


    Dommage de devoir perdre ta participation, tout du moins sur cette discussion.

    J’espère quand même que j’aurai l’occasion de te retrouver, lors d’un autre débat.

    Je tenais néanmoins à te remercier pour tout ce que tu m’as appris, même si pour le moment, je ne partage pas complètement tes dernières explications.

    D'autant plus que j'ai trouvé ce qui suit dans une doc (que je lis.. avant de poster) qui se trouve ici.

    Un site, qui je pense est quand même sérieux... (www.gnu.org/)

    Never call g-object-unref unless you have previously called g-object-ref, even if you created the <gtk-object>.

    (Note: this is not true for <gobject>; for <gobject>, the creator of the object owns a reference.)
    Que je traduis de la façon suivante:

    Ne jamais appeler g-objet-unref(), sauf si vous avez déjà appelé g-objet-ref(), même si vous avez créé un <gtk-object>.

    Remarque: Cela ne vaut pas pour un <gobject>, car pour un <gobject>, le créateur de l'objet possède bien une référence.


    Ce qui, si je comprends bien, semble corroborer ce que j'ai dit précédemment ?
    Ou alors ... j'ai rien compris!

    .... ...

    Je pense avoir réussi, entre autre grâce à ta participation, à dégrossir pas mal le sujet sur la mémoire.

    C'est maintenant dans mon esprit, un peu plus clair.

    Merci à toi et @+++... j’espère.


    Claude

  20. #20
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Désolé, c'est juste que ça prend beaucoup de temps de lire, comprendre ce que vous voulez dire, tester les différents exemples... Et là les supputations finissent par ne rien apporter de plus. Pas de soucis pour intervenir sur d'autres débats bien sûr, mais là ça me commençait à me prendre trop de temps.

    Pour ton lien, oui le site GNU est sérieux, la documentation Guile aussi, mais GtkObject n'existe plus depuis des lustres. Il a été déprécié dans GTK 2, et complètement retiré dans GTK 3. Je pense toutefois que le conseil est correct: si tu n'as pas fait toi même de g_object_ref ou g_object_ref_sink, n'appelle pas g_object_unref de toi même, mais plutôt gt_widget_destroy.

    @+
    Documentation officielle GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels GTK+ 3:
    GTK en C, GTK en Python

    Tutoriels par l'exemple (platform-demos):
    GTK (tous langages)

Discussions similaires

  1. Comment libérer la mémoire encore occupée par des processus terminés ?
    Par Jipété dans le forum Administration système
    Réponses: 16
    Dernier message: 06/11/2015, 00h03
  2. Pb avec des fonctions pour libérer la mémoire
    Par Krisprolls31 dans le forum C
    Réponses: 3
    Dernier message: 26/08/2010, 14h14
  3. Comment libérer la mémoire d'un TList ?
    Par Tchaill39 dans le forum Langage
    Réponses: 8
    Dernier message: 16/11/2005, 17h53
  4. Réponses: 2
    Dernier message: 14/12/2004, 18h42
  5. Gestion mémoire des Meshes (LPD3DXMESH)
    Par [Hideki] dans le forum DirectX
    Réponses: 1
    Dernier message: 08/07/2003, 20h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo