Bonjour,
Auriez vous un exemple simple de code java pour la fuite de memoire?
Merci
Bonjour,
Auriez vous un exemple simple de code java pour la fuite de memoire?
Merci
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"
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.
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 :
Il faut faire :
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 } }
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; }
Salut,
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 :
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 !!!
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(); } }
(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 :
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).
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(); }
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++
Je ne sais pas sous quel item mais ça mériterait d'être conservé dans la FAQ ça ...
![]()
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++
très bon tuto! merci pour ce cours![]()
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.
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 ?
Partager