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++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    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
    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 308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Côte d'Or (Bourgogne)

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

    Informations forums :
    Inscription : Février 2008
    Messages : 2 308
    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 éclairé
    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
    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
    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.

  5. #5
    Membre éclairé
    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
    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 chevronné 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 : 30
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    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.

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