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

C# Discussion :

Question sur la libération de mémoire


Sujet :

C#

  1. #1
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut Question sur la libération de mémoire
    Bonjour,

    Je me pose des questions sur la libération correcte de la mémoire. (VS 2008, ici sur une appli WCF)


    Sur cet exemple, comment faire pour être sûr que la mémoire est totalement libérée ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    using System.IO;
    [...]
      /// <summary>
      /// Renvoie la date de dernière modification du fichier
      /// </summary>
      public DateTime LastModif
      {
        get
        {
            return new FileInfo(FilePathName).LastWriteTime;
        }
      }
    [...]
    Est-ce que ce code serait mieux ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      public DateTime LastModif
      {
        get
        {
          FileInfo fi = new FileInfo(FilePathName);
          try
          {
            return fi.LastWriteTime;
          }
          finally
          {
            fi = null;
          }
        }

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2005
    Messages : 700
    Par défaut
    Citation Envoyé par adaneels Voir le message
    Sur cet exemple, comment faire pour être sûr que la mémoire est totalement libérée ?
    En faisant confiance au Garbage collector :-)
    Sachant que DateTime est en + un type valeur, tu ne conserveras aucune référence à FileInfo par la suite => GC 0 direct.

    Il faut faire attention à tout objet héritant de IDisposable, et éviter certaines boucles qui bourrinent dans le GC inutilement. Pour la libération mémoire des IDisposable, il est convenu d'utiliser le
    using(IDisposable){ ... }


    Le Try finally est ici + qu'inutile, ce qui importe par contre dans cet exemple ce sont les possibles exceptions du à un mauvais Path :
    Exceptions:
    System.ArgumentNullException: fileName is null.
    System.Security.SecurityException: The caller does not have the required permission.
    System.ArgumentException: The file name is empty, contains only white spaces, or contains invalid characters.
    System.UnauthorizedAccessException: Access to fileName is denied.
    System.IO.PathTooLongException: The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
    System.NotSupportedException: fileName contains a colon ( in the middle of the string.
    Si ton Path est un membre privé, il serait préférable de directement conservé une référence à FileInfo, ou alors de le créer au Set de FilePathName. Enfin, tout est question de choix.

    Cela dit, si une exception pète, il y a un problème : il faut le traiter d'une façon ou d'une autre...

  3. #3
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Yep, comme le dit Chubyone, FileInfo n'implémente pas IDisposable : c'est donc qu'il ne détient aucune ressource non managée, et que donc qu'il n'y à rien à faire.

    La mémoire est une ressource managée, donc on y touche pas (plus précisement, on peut pas ), et on laisse le GC nettoyer les objets morts quand il en a envie.

  4. #4
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    OK.
    Je me posais la question parce qu'il y avait un new et venant de Delphi, j'ai l'habitude de penser "Tout Create doit avoir son Free.".

    Pour les using et dispose, oui oui, je fais gaffe

    Lorsque je regarde tout ce qu'il se dit sur Internet sur "comment éviter les fuites de mémoire" ou "comment bien libérer la mémoire utilisée", je vois beaucoup "mettez vos variables à null" !

    Hors, à moins d'avoir un type nullable, il n'y a que les string, object et les collections que l'on peut mettre à null (à ma connaissance mais je n'ai pas testé tous les types ).
    Mais ça me paraît bizarre de mettre à null tous mes strings avant de sortir d'une fonction...
    Ces objets sont pourtant bien managés et ils sont bien libérés (en supposant qu'il n'y ait que des variables locales) au prochain passage du GC après que la fonction ait été exécutée, non ?

    Arnaud DANEELS
    un développeur qui se pose des questions sur la gestion de mémoire en C# alors que c'était plus simple en Delphi ou en C : on alloue et on désalloue soi-même.

  5. #5
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 204
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2005
    Messages : 700
    Par défaut
    Il y a deux "types" en .Net : les types valeurs, int, float, DateTime, ou Struct plus généralement, et les types références : tout le reste.

    Renseignes toi sur la MSDN il y a pas mal d'explications la dessus

    Puisque tu en parles il y a une précision importante a BIEN retenir : le type string est IMMUTABLE : toute modification entraine une nouvelle instance. De cette connaissance, à toi de savoir quoi faire, et surtout NE PAS faire
    La dessus aussi la MSDN a de quoi t'offrir pleins d'explications

  7. #7
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    OK, je vais mettre le nez dans la MSDN plus en détail demain.

    merci

  8. #8
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par adaneels Voir le message
    Lorsque je regarde tout ce qu'il se dit sur Internet sur "comment éviter les fuites de mémoire" ou "comment bien libérer la mémoire utilisée", je vois beaucoup "mettez vos variables à null" !
    Ce qui ne sert à rien, à par pourrir le code de lignes inutiles
    Mettre à null, ce n'est que couper le lien entre une référence et l'objet qu'elle référençait. Ca ne libère pas l'objet.
    Pourquoi ne peut on pas libérer la mémoire à la main ? Ben c'est simple : un objet peut être référencé par un paquet d'objets vivants. Si on pouvait le libérer (via un "delete uneDesRéférences"), ça voudrait dire que toutes ces références seraient invalides. Et en C#, t'as l'assurance que toute référence est valide.

    Donc, répète après moi, comme un mantra : j'oublie tout ce que je sais de la gestion de la mémoire en C ou Delphi, et je ne m'occupe que des autres ressources (connexion réseau, handle, etc.) en C# avec le pattern IDisposable

  9. #9
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Donc, répète après moi, comme un mantra : j'oublie tout ce que je sais de la gestion de la mémoire en C ou Delphi, et je ne m'occupe que des autres ressources (connexion réseau, handle, etc.) en C# avec le pattern IDisposable
    OK OK lol
    je suis repasser dans les lignes que j'ai modifié hier pour enlever ces nulls . J'avais fait ça pour voir si ça améliorer quelquechose mais ça me plaît bien mieux sans ces null partout


    Deux autres questions :
    - les collections : je suppose que l'on n'a pas besoin de les vider sauf si le type de données dérive de IDisposable. j'ai juste ?
    - Si j'ai bien compris la politique en C# : ce type-ci n'a pas besoin d'avoir son Dispose :
    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
    public class TOCLogItem : INotifyPropertyChanged
    {
      private DateTime _Dt;
      private TOCLogGravity _Gravity;
      private string _Message;
     
      public DateTime Dt
      {
        get { return _Dt; }
        set
        {
          _Dt = value;
          if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Dt"));
        }
      }
      public TOCLogGravity Gravity
      {
        get { return _Gravity; }
        set
        {
          _Gravity = value;
          if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Gravity"));
        }
      }
      public string Message
      {
        get { return _Message; }
        set
        {
          _Message = value;
          if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Message"));
        }
      }
     
      public TOCLogItem(DateTime dt, TOCLogGravity gravity, string message)
      {
        this.Dt = dt;
        this.Gravity = gravity;
        this.Message = message;
      }
     
      public event PropertyChangedEventHandler PropertyChanged;
    }
    j'ai juste ?

  10. #10
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    et tant qu'à faire, question sur le code managé / non managé : comment reconnaître si un code est managé ou non ?

    A la question "qu'est-ce c'est?", c'est bon (je le mets pour les lecteurs de passage au cas où) : en simple : code managé = traité par le CLR, code managé = directement traité par le processeur.

    Mais comment savoir, lorsque l'on développe, si on utilise du code managé ou non managé ?
    Parce que les "handles de fenêtre, connexions réseau, etc...", c'est bien mais faut connaître par coeur la lsite exhaustives des types non managés pour faire ça correctement du coup
    Exemple avec le type StreamWriter : on se dit, bah on fait F1 et MSDN nous le dira, ben non, on tombe sur "Comment : écrire des données de classes dans un fichier XML" (si on mets juste le curseur sur le mot, faut surligner en fait pour tomber sur la classe :-p).
    Dans l'aide sur le StreamWriter, on ne voit nul part "ceci est managé" ou "ceci est non-managé".

    Par contre, on voit que le StreamWriter a une méthode Dispose() (donc il a quelquechose à libérer que le GC ne saura pas libérer à priori).
    Est-ce que cette condition est suffisante pour savoir si j'utilise des objets managés ou non-managés ?

    Et par extension, lorsque je créée ma propre classe et que je me pose la question "Est-ce que je dois lui ajouter un Dispose() ?", la réponse va être oui si et seulement si la mémoire d'un objet non-managé utilisé dans la classe doit être libéré au moment de la destruction de mon instance de classe ?
    C'est bien ça ?

  11. #11
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    Merci pour le lien .

    Ca me rassure

  12. #12
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    Citation Envoyé par Chubyone Voir le message
    Puisque tu en parles il y a une précision importante a BIEN retenir : le type string est IMMUTABLE : toute modification entraine une nouvelle instance. De cette connaissance, à toi de savoir quoi faire, et surtout NE PAS faire
    Thanks, je ne savais pas

    [edit : preuve que je lis bien la MSDN : le type string est IMMUABLE ;-) ]

  13. #13
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    Citation Envoyé par Chubyone Voir le message
    Le Try finally est ici + qu'inutile
    Ca me rassure grandement de ne pas avoir à pourrir mon code

    Citation Envoyé par Chubyone Voir le message
    Si ton Path est un membre privé, il serait préférable de directement conservé une référence à FileInfo, ou alors de le créer au Set de FilePathName. Enfin, tout est question de choix.
    Bien deviné, c'est une propriété privée.
    Dans le cas présent, le Path est utilisé pour d'autres choses et ne sert dans un FileInfo que pour cette méthode.
    Merci pour le conseil tout de même, tout conseil est le bienvenue.

  14. #14
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par adaneels Voir le message
    Par contre, on voit que le StreamWriter a une méthode Dispose() (donc il a quelquechose à libérer que le GC ne saura pas libérer à priori).
    Ce n'est pas qu'il sait pas le libérer ; c'est qu'il ne libèrerait qu'à le destruction en mémoire de l'objet, ce qui peut n'importe quand.
    Est-ce que cette condition est suffisante pour savoir si j'utilise des objets managés ou non-managés ?
    Tu confonds deux choses.
    Précisons un peu ce que l'on entend par "managé"
    En C#, à l'exception de code inclus dans un bloc "unsafe", on travaille en environnement 100% managé. C'est à dire que toutes les classes le sont, qu'il n'y a pas de libération mémoire possible, qu'on ne peux pas accéder aux pointeurs, etc.
    En C++ /CLI, tu peux mélanger du code C++ natif (non managé, donc) et du code managé.

    Après, il y a autre chose, c'est le pattern IDisposable. Une classe implémente l'interface IDisposable si elle doit libérer une ressource quand on a plus besoin d'elle.
    Attention : la méthode Dispose n'est pas appelée lorsque l'objet est détruit en mémoire par le GC. Elle est purement déterministe, et se déclenche que si tu l'appelles à la main, ou lors de l'utilisation d'un bloc using.

    Donc, la gestion de ressource en C# consiste à oublier la mémoire, et à disposer les classes encapsulant d'autres types de ressources.
    Et par extension, lorsque je créée ma propre classe et que je me pose la question "Est-ce que je dois lui ajouter un Dispose() ?", la réponse va être oui si et seulement si la mémoire d'un objet non-managé utilisé dans la classe doit être libéré au moment de la destruction de mon instance de classe ?
    C'est bien ça ?
    Tu dois implémenter IDisposable si ta classe a une variable membre qui est disposable, ou si elle même détient une ressource. Ce n'est pas une question de managé ou non managé. Si t'as un fichier d'ouvert en lecture, il faut bien le fermer quand tu as fini de t'en servir.

    Par exemple, si tu te crées un protocole perso de comm client serveur, à base de sockets (chose à pas faire la plupart du temps, mais bon ), tu peux faire un this.socket.Close() dans le dispose de la classe qui wrappe ta connexion.

  15. #15
    Membre éclairé
    Avatar de adaneels
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Août 2006
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Août 2006
    Messages : 236
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Ce n'est pas qu'il sait pas le libérer ; c'est qu'il ne libèrerait qu'à le destruction en mémoire de l'objet, ce qui peut n'importe quand.Tu confonds deux choses.
    Précisons un peu ce que l'on entend par "managé"
    En C#, à l'exception de code inclus dans un bloc "unsafe", on travaille en environnement 100% managé. C'est à dire que toutes les classes le sont, qu'il n'y a pas de libération mémoire possible, qu'on ne peux pas accéder aux pointeurs, etc.
    OK, je m'enlève de la tête "... n'implémente pas IDisposable : c'est donc qu'il ne détient aucune ressource non managée", c'est ce qui m'a fait faire le lien entre idsposable et code non managé je pense.

    Citation Envoyé par Guulh Voir le message
    Après, il y a autre chose, c'est le pattern IDisposable. Une classe implémente l'interface IDisposable si elle doit libérer une ressource quand on a plus besoin d'elle.
    OK
    Citation Envoyé par Guulh Voir le message
    Attention : la méthode Dispose n'est pas appelée lorsque l'objet est détruit en mémoire par le GC. Elle est purement déterministe, et se déclenche que si tu l'appelles à la main, ou lors de l'utilisation d'un bloc using.
    Oui oui, ça, j'avais compris et j'y fait très attention, c'est vraiment savoir quand la mettre en place que j'ai du mal à comprendre.

    Citation Envoyé par Guulh Voir le message
    Donc, la gestion de ressource en C# consiste à oublier la mémoire, et à disposer les classes encapsulant d'autres types de ressources.
    Tu dois implémenter IDisposable si ta classe a une variable membre qui est disposable, ou si elle même détient une ressource. Ce n'est pas une question de managé ou non managé. Si t'as un fichier d'ouvert en lecture, il faut bien le fermer quand tu as fini de t'en servir.
    OK, donc au final, c'est une histoire de bon sens. (tout flux ouvert se ferme, toute connexion ouverte se ferme sauf si besoin de pool bien sûr, toute ressource héritant de IDisposable doit être "disposée".

    Citation Envoyé par Guulh Voir le message
    Par exemple, si tu te crées un protocole perso de comm client serveur, à base de sockets (chose à pas faire la plupart du temps, mais bon ), tu peux faire un this.socket.Close() dans le dispose de la classe qui wrappe ta connexion.
    OK, ça marche


    Dernière question (je pense ) : dans l'exemple de cette page MSDN http://msdn.microsoft.com/fr-fr/library/ms182269.aspx, la connexion est mise à null après l'appel à Dispose().
    Si j'ai bien compris : mettre à null supprime la référence vers la valeur. Lorsque le GC passera, il verra que plus personne ne pointe sur cette valeur, il libèrera la mémoire.
    Si on fait un Dispose() sur un objet, vaut mieux le mettre à null tout de suite derrière ?

  16. #16
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2005
    Messages : 700
    Par défaut
    Citation Envoyé par adaneels Voir le message
    Si on fait un Dispose() sur un objet, vaut mieux le mettre à null tout de suite derrière ?
    Il est mis à null car il y a le test if null juste avant : si tu appel une seconde fois Dispose, le test ne passeras pas. Sinon tu vas tenter de Dispose 2 fois _Connection, et il ne va pas aimer du tout :-)

  17. #17
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par Chubyone Voir le message
    Il est mis à null car il y a le test if null juste avant : si tu appel une seconde fois Dispose, le test ne passeras pas. Sinon tu vas tenter de Dispose 2 fois _Connection, et il ne va pas aimer du tout :-)
    Yep, vu que bien que l'idempotence de Dispose est fortement conseillée mais ni obligatoire ni vérifiable dans du code tiers, vaut mieux s'assurer soi-même que des appels successifs ne fassent pas tout sauter.
    Perso, je suis trop amoureux du mot clé readonly pour mettre à null des variables membres de ce style

    Après, si la variable disposable est déclarée dans le corps d'une méthode et destinée à mourir dans cette même méthode, mettre à null ne sert à rien. Et de plus, autant profiter du mot clé using :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public int GetLaData()
    {
      using (DataConnection conn = new dataConnection())
      {
        return conn.Gnagnagna();
      } // en sortie de ce bloc, l'objet est disposé
    }
    L'objet est disposé quelle que soit la façon dont on quitte le bloc (return, exception, mais pas coupure de courrant, quand même )

Discussions similaires

  1. questions sur la libération d'ingrid de bétancourt
    Par shadowmoon dans le forum Politique
    Réponses: 5
    Dernier message: 03/09/2008, 21h08
  2. Question sur l'allocation de mémoire
    Par Fonzy007 dans le forum Linux
    Réponses: 8
    Dernier message: 26/12/2006, 10h29
  3. Quelques questions sur la mémoire
    Par Gruik dans le forum C
    Réponses: 6
    Dernier message: 17/11/2004, 15h38
  4. Question simple sur la libération des objets
    Par gibet_b dans le forum Langage
    Réponses: 2
    Dernier message: 12/07/2004, 11h01
  5. Réponses: 25
    Dernier message: 16/07/2003, 21h41

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