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

Langage Java Discussion :

[ fuite ] memoire


Sujet :

Langage Java

  1. #1
    Membre régulier
    Inscrit en
    Juillet 2003
    Messages
    229
    Détails du profil
    Informations forums :
    Inscription : Juillet 2003
    Messages : 229
    Points : 90
    Points
    90
    Par défaut [ fuite ] memoire
    Bonjour,

    Auriez vous un exemple simple de code java pour la fuite de memoire?




    Merci
    hocine

  2. #2
    Membre chevronné
    Avatar de CheryBen
    Inscrit en
    Mai 2005
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Âge : 42

    Informations forums :
    Inscription : Mai 2005
    Messages : 1 599
    Points : 2 197
    Points
    2 197
    Par défaut
    Exemple tiré de la FAQ
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    String resultat = ""; // création d'un chaine vide
    for( int i=0; i<10; i++) {
            resultat = resultat + i;
    } 
    System.out.println(resultat); // "0123456789"

  3. #3
    Membre actif Avatar de mOuLi
    Homme Profil pro
    Développeur Java
    Inscrit en
    Avril 2008
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Avril 2008
    Messages : 170
    Points : 257
    Points
    257
    Par défaut
    Voici un article qui illustre la problématique des fuites mémoires en Java (et qui propose une solution en utilisant les WeakReference Java).
    Par contre si on veut rester précis, le terme "fuite mémoire" (memory leak) est plutôt un abus de langage en Java (par rapport aux fuites mémoire de langages non managés comme C ou C++ par exemple) : voir début de cet article.

  4. #4
    Membre expérimenté Avatar de herve91
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    1 282
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 282
    Points : 1 608
    Points
    1 608
    Par défaut
    Citation Envoyé par morph41 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    String resultat = ""; // création d'un chaine vide
    for( int i=0; i<10; i++) {
            resultat = resultat + i;
    } 
    System.out.println(resultat); // "0123456789"
    Ce code ne provoque pas de fuite mémoire.

  5. #5
    Membre chevronné
    Avatar de CheryBen
    Inscrit en
    Mai 2005
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Âge : 42

    Informations forums :
    Inscription : Mai 2005
    Messages : 1 599
    Points : 2 197
    Points
    2 197
    Par défaut
    Citation Envoyé par herve91 Voir le message
    Ce code ne provoque pas de fuite mémoire.
    A chaque itération on créé une nouvelle String inutile en mémoire, suivant la taille de la boucle la perte peut être importante. On ne peut pas considérer ça comme une fuite mémoire?

  6. #6
    Membre expérimenté Avatar de herve91
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    1 282
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 282
    Points : 1 608
    Points
    1 608
    Par défaut
    Non, car la mémoire est toutefois récupérée par le GC.
    Une fuite mémoire, c'est quand un objet n'est plus accessible "logiquement" de l'application, mais est toujours référencé par un autre objet.

    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
    class Stack {
     
        private Object[] list;
        int sp;
     
        public Stack(int size) {
           list = new Object[size];
        }
     
        public push(Object o) {
           list[sp++] = o;
        }
     
        public Object pop() {
           return list[--sp]; // Ici on garde toujours la référence de l'objet 
        }
    }
    Il faut faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        public Object pop() {
           Object o = list[--sp];
           list[sp] = null; // !!!
           return o;
        }

  7. #7
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Citation Envoyé par morph41 Voir le message
    A chaque itération on créé une nouvelle String inutile en mémoire, suivant la taille de la boucle la perte peut être importante. On ne peut pas considérer ça comme une fuite mémoire?
    Non pas vraiment : c'est plutôt une consommation abusive (et surtout inutile) d'objets temporaires, et donc une grosse consommation de mémoire. C'est bien d'éviter cela mais ce n'est pas une fuite

    A la fin de la boucle les objets temporaires peuvent légitimement être nettoyé par le GC.



    Comme l'a signalé mOuLi les fuites de mémoire n'existe pas vraiment en Java pure, puisque dans les autres langages il s'agit généralement de pointeur dont on perd la valeur et qu'on ne peut donc plus libérer.
    En Java lorsqu'on perd la valeur d'une référence, le GC s'occupera de libérer l'objet...




    En fait en Java les fuites de mémoire correspondent plutôt à l'inverse, c'est à dire lorsqu'on conserve des références à des objets que l'on ne va plus utiliser, ou que l'on conserve un trop grand nombre d'objet...







    Ainsi la majorité des "fuites" vient du fait que l'on n'utilise pas correctement le bon scope pour une variable, le plus souvent par facilité ou parce qu'on croit par erreur économiser la mémoire en économisant les variable, par 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
    class Tools {
    	private static StringBuilder buf = null;
     
    	/**
             * Crée une chaine de caractère de n charactère c
             * @param n Le nombre de caractère de la chaine
             * @param c Le caractère utilisé pour initialisé la chaine
             * @return
             */
    	public static String makeString(int n, char c) {
    		buf = new StringBuilder(n);
    		for (int i=0; i<n; i++) {
    			buf.append(c);
    		}
    		return buf.toString();
    	}
     
    	/**
             * Crée une chaine composé de plusieurs lignes
             * @param strings Chaque paramètre correspond à une ligne
             * @return
             */
    	public static String join(String...strings) {
    		buf = new StringBuilder();
    		for (String s : strings) {
    			buf.append(s).append(System.getProperty("line.separator"));
    		}
    		return buf.toString();
    	}
    }
    Ici la variable buf est mise en static car elle est "utilisé" dans deux méthodes. Or c'est une erreur : oci buf est bel et bien une variable temporaire et devrait le rester !!!
    (note : on retrouve également ce type d'erreur avec des variable d'instance...)


    Pourquoi ? C'est simple prenons le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	public void maMethod() {
    		String s = Tools.makeString(500000, 'a');
     
    		// ...
    	}
    C'est tout bête, on crée une chaine de 500000 'a', qui sera proprement libérable à la fin de la méthode... toutefois le champs static et privée Tools.buf existera toujours pour rien, et cela jusqu'à ce qu'on utilise une autre des méthodes static de Tools

    Le fait de mettre buf à null à la fin de chaque méthode n'est pas non plus une solution... c'est plutôt casse-gueule (le risque d'oubli est important).


    La solution est bien sûr d'utiliser une variable locale, ce qui permet de limiter fortement le scope de l'objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	/**
             * Crée une chaine de caractère de n charactère c
             * @param n Le nombre de caractère de la chaine
             * @param c Le caractère utilisé pour initialisé la chaine
             * @return
             */
    	public static String makeString(int n, char c) {
    		final StringBuilder buf = new StringBuilder(n);
    		for (int i=0; i<n; i++) {
    			buf.append(c);
    		}
    		return buf.toString();
    	}
    A la fin de la méthode buf sera libérable par le GC.
    En plus cela permet de déclarer la variable final et donc de bénéficier potentiellement de quelques petites optimisations du couple compilateur/JVM




    Bref je dirais que la règle numéro 1 est de bien vérifier le scope des variables, et de le réduire au strict minimum ! Cela peut sembler anodin mais sur une grosse application cela peut avoir une grosse importance !







    Ensuite une grosse source d'erreur vient de la gestion des ressources systèmes ou externes (fichiers, sockets, connexion JDBC, ...) qui ne peuvent pas être gérée par le GC.

    Exemple : la lecture d'un fichier via une URL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    	public static String read(URL url) throws IOException {
    		Reader reader = new InputStreamReader(url.openStream());
    		StringBuilder buf = new StringBuilder();
    		char[] cbuf = new char[8192];
    		int len;
     
    		while ( (len = reader.read(cbuf)) >= 0) {
    			buf.append(cbuf, 0, len);
    		}
    		return buf.toString();
    	}
    Ici on ne ferme jamais la socket ouverte par url.openStream(), et elle reste donc ouverte même après la fin de la méthode (je parle de la socket native et non pas de l'objet Java).

    Note : en vérité la socket pourrait être fermée indirectement car il y a généralement un "garde-fou" basé sur la finalisation, mais il ne faut pas se baser dessus : ces objets représentent peu d'occupation mémoire pour le GC, mais peuvent engendrer de grosses ressources systèmes invisible par le GC, et du coup la finalisation pourrait intervenir trop tard...


    Pour régler le problème il faut bien sûr libérer les ressources via la méthode close(), mais il faut utiliser un bloc try/finally sinon on peut passer au travers (lire : Comment libérer proprement les ressources (ou comment utiliser proprement les bloc try/finally) ?).

    Ce qui donnerait :
    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
    	public static String read(URL url) throws IOException {
    		Reader reader = new InputStreamReader(url.openStream());
    		try {
    			StringBuilder buf = new StringBuilder();
    			char[] cbuf = new char[8192];
    			int len;
     
    			while ( (len = reader.read(cbuf)) >= 0) {
    				buf.append(cbuf, 0, len);
    			}
    			return buf.toString();
    		} finally {
    			// Quoi qu'il arrive (return ou exception)
    			// On ferme le flux :
    			reader.close();
    		}
    	}








    Enfin en ce qui concerne les WeakReferences et autres, ce n'est pas vraiment la solution absolu aux fuites de mémoire, mais correspondent plutôt à des solutions bien spéciques...

    • Les WeakReferences permet de conserver une référence sur un objets sans empêcher sa libération lorsque l'objet n'est plus référencé ailleurs la WeakReference devient invalide et l'objet peut être libéré...

      On peut prendre comme exemple les WeakHashMap qui permettent de conserver des valeurs tant que la clef est valide...
    • Les SoftReference sont quand à elles destiné à conserver des objets en mémoire afin d'éviter de les recréer, sans pour autant que cela ne cause d'OutOfMemory.

      En fait lorsqu'on a une SoftReference le GC ne détruira l'objet qu'en dernier recours, et il le gardera tant que possible. C'est parfait pour faire un cache de données : on conserve les données même si elle ne sont plus utilisé, mais si l'espace mémoire manque le GC détruira ces données...



    Plus d'info sur le blog : Comprendre les références en Java


    a++

  8. #8
    in
    in est déconnecté
    Membre expérimenté Avatar de in
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    1 612
    Détails du profil
    Informations personnelles :
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Avril 2003
    Messages : 1 612
    Points : 1 718
    Points
    1 718
    Par défaut
    Je ne sais pas sous quel item mais ça mériterait d'être conservé dans la FAQ ça ...

    "If email had been around before the telephone was invented, people would have said, 'Hey, forget email! With this new telephone invention I can actually talk to people!"

    Besoin d'une nouvelle méthode pour développer ? -> http://www.la-rache.com/

  9. #9
    Membre averti Avatar de Chatbour
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2006
    Messages
    431
    Détails du profil
    Informations personnelles :
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2006
    Messages : 431
    Points : 305
    Points
    305
    Par défaut
    Salut à tous

    Citation Envoyé par adiGuba Voir le message
    En plus cela permet de déclarer la variable final et donc de bénéficier potentiellement de quelques petites optimisations du couple compilateur/JVM
    d'abord, chapeau adiGuba : +1 in

    ma question : en quoi représente les final un gain en optimisation ?

  10. #10
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Chatbour Voir le message
    ma question : en quoi représente les final un gain en optimisation ?
    La variable ne sera jamais modifié : le compilateur et/ou la JVM peuvent donc effectuer des optimisations plus "agressive".

    Pour un attribut d'instance la JVM évite de vérifier la valeur de la référence à chaque fois, et les types primitifs pourraient être remplacés par des constantes...

    Sans oublier que cela apporte une information très important dans le code : on sait que la valeur/référence ne sera jamais modifié !

    A utiliser autant que possible

    a++

  11. #11
    Membre averti Avatar de Chatbour
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2006
    Messages
    431
    Détails du profil
    Informations personnelles :
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2006
    Messages : 431
    Points : 305
    Points
    305
    Par défaut
    Merci bien

  12. #12
    Membre régulier
    Inscrit en
    Décembre 2006
    Messages
    84
    Détails du profil
    Informations forums :
    Inscription : Décembre 2006
    Messages : 84
    Points : 73
    Points
    73
    Par défaut
    très bon tuto! merci pour ce cours

  13. #13
    Membre du Club
    Inscrit en
    Avril 2011
    Messages
    58
    Détails du profil
    Informations forums :
    Inscription : Avril 2011
    Messages : 58
    Points : 67
    Points
    67
    Par défaut
    adiGuba : super interessant ce post. J'ai dû chercher pour tomber dessus, ce serait bien de l'avoir dans une page "les bonnes pratiques java". Je vais chercher, elle existe peut être déjà cette page.

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 147
    Points : 65
    Points
    65
    Par défaut
    Merci pour ce post très intéressant.
    Je rencontre justement une fuite de mémoire très gênante, qui bloque mon application au bout d'une vingtaine d'itérations.
    J'ai compris à peu près les principes énoncés ci-dessus pour éviter les fuites, mais je voudrais bien localiser la partie de mon code responsable.
    J'ai vu sur des posts anciens ou des FAQ qu'il existait des outils à installer qui permettaient d'explorer la structure de la mémoire pour identifier les objets inutilement conservés.
    Quel est l'état le récent de la science en la matière ? les derniers outils les plus simples à prendre en main ?
    Les dernières versions d'Eclipse intègrent-elles de tels outils ?

Discussions similaires

  1. Réponses: 4
    Dernier message: 31/10/2008, 09h31
  2. fuite memoire avec sfml
    Par themulot dans le forum SFML
    Réponses: 2
    Dernier message: 27/11/2007, 11h25
  3. [2.0] Comment tracer les fuites mémoire ?
    Par StormimOn dans le forum C++/CLI
    Réponses: 9
    Dernier message: 02/11/2007, 08h22
  4. [java] fuite memoire quand 2 process!
    Par vince3320 dans le forum Langage
    Réponses: 6
    Dernier message: 01/03/2007, 17h50
  5. [VC++6] probleme fuite memoire CLIST ?
    Par athilla dans le forum MFC
    Réponses: 16
    Dernier message: 22/11/2005, 17h01

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