2009-08-15 7 views
48

Quelles classes de l'API Java Standard peuvent provoquer des fuites de mémoire lorsqu'elles sont utilisées de manière (pas évidemment) incorrecte? Et comment ces fuites de mémoire peuvent-elles être évitées/réparées?Pièges de fuite de mémoire dans l'API Java Standard

Exemple:ObjectInputStream et ObjectOutputStream garder les références à tous les objets qu'ils ont vu pour envoyer occurences ultérieures du même objet que des références plutôt que des copies (et donc traiter avec des références circulaires). Cela provoque une fuite de mémoire lorsque vous gardez un tel flux ouvert indéfiniment (par exemple, lorsque vous l'utilisez pour communiquer sur le réseau).

Correction: Appelez reset() périodiquement ou après chaque objet de niveau supérieur.

+3

@Michael - peut-être déplacer votre exemple à une réponse par lui-même? –

Répondre

46

Un gros problème est que l'obtention de sous-chaînes de chaînes Java fait référence à la chaîne d'origine. Exemple: vous lisez dans un enregistrement de 3000 caractères et vous obtenez une sous-chaîne de 12 caractères, renvoyée à l'appelant (dans la même machine virtuelle Java). Même si vous n'avez pas directement une référence à la chaîne d'origine, cette chaîne de 12 caractères utilise encore 3000 caractères en mémoire.

Pour les systèmes qui reçoivent puis analysent beaucoup de messages, cela peut être un réel problème.

Vous avez deux façons d'éviter ce:

String sub = new String(str.substring(6,12)); 

ou

String sub = str.substring(6,12).intern(); 

Le premier est plus clearcut. La seconde a d'autres implications parce que vous utilisez l'espace PermGen. Vous risquez d'être surexploité à moins de donner assez de temps à votre machine virtuelle. Rappelez-vous que ceci n'est pertinent que si vous prenez de petites sous-chaînes et que vous jetez ensuite la ficelle d'origine, ce que vous faites souvent.

+1

Que diriez-vous de mentionner une simple correction telle que la création d'un nouvel objet String explicitement? –

+0

Ne pas cogner un très vieux fil mais, quelle est la raison pour cela? Pourquoi cette ligne "str = str.substring (6,12)" perdrait-elle un espace de 3k caractères au lieu de seulement 6? –

+2

@Murat: cogner un vieux commentaire: parce qu'il se réfère en interne au même tableau de caractères que la chaîne d'origine, juste avec un décalage et une longueur différents. Si vous créez beaucoup de sous-chaînes qui se chevauchent, cela économise de la mémoire. –

4

Toute classe avec la méthode dispose()?

Par exemple java.awt.Window#dispose:

public void (disposer)

Libère toutes les ressources utilisées par écran natif de cette fenêtre, ses sous-composants, et tous ses enfants appartenant. Autrement dit, les ressources de ces composants seront détruites, toute mémoire qu'ils consomment sera renvoyée au système d'exploitation, et ils seront marqués comme indisponibles.

8

Tout ce que vous enregistrez en tant que destinataire d'un événement, par ex. Dans les frameworks GUI, ne peut pas être collecté en mémoire tant qu'il est enregistré et que la source d'événement est active.

Ceci peut introduire des fuites de mémoire, si un développeur n'est pas conscient de cette forte référence de la source d'événement à l'abonné à l'événement.

+0

C'est une bonne idée d'utiliser des références faibles pour tout auditeur qui devrait vivre plus longtemps que ce qu'il écoute. –

+0

Ceci est presque l'exemple standard pour une fuite de mémoire, car il est évident mais facile à oublier. –

5

Chaque instanciation d'un thread alloue de la mémoire pour une pile (par défaut 512k, accordable via -Xss).Ce n'est pas une fuite en tant que tel, mais une tentative naïve de multi-threader fortement une application entraînera une consommation de mémoire non évidente importante.

+3

En fait, il peut se transformer en une fuite pure et simple si vous créez des objets Thread et que vous n'appelez jamais start() - la mémoire de la pile n'est alors jamais récupérée, IIRC. –

+0

Intéressant. Je ne le savais pas. –

+2

@Michael Borgwardt: Je ne pense pas que ce soit le cas; Avez-vous une source? \ [edit \]: il semble que cela ait pu être un bug dans les anciennes versions de la JVM, mais cela devrait être corrigé dans Java 5. – Miles

7

Toutes les classes internes non statiques que vous pouvez conserver dans les classes externes. Donc, cette classe intérieure à l'air innocent peut contenir un grand graphique . Placez l'instance dans une collection statique ou à l'échelle de l'application quelque part et vous utilisez un lot de mémoire que vous n'êtes pas censé utiliser.

3

Utilisation de références fortes lorsque les références faibles auraient été suffisantes. Les applications et les API qui exécutent leur propre gestion des états et des ressources sont les coupables habituels ici. Ensuite, il y a l'utilisation du modèle Observer, qui peut entraîner des fuites de mémoire - lorsque l'observateur se retire du sujet, la mémoire ne peut être récupérée que si le sujet libère également la référence à l'observateur/écouteur. Cela a déjà été souligné plus tôt, mais peu de gens réalisent que même les bûcherons sont des observateurs.

De plus, there is the likelihood of classes, whose objects when instantiated are automatically placed into a static member. Certaines de ces classes n'ont même pas de méthode release() ou dispose(), donc les références continuent à être détenues par le membre statique. Cette variété de fuites de mémoire entraîne finalement une erreur OutOfMemory avec un problème d'espace PermGen signalé comme cause principale, ce qui rend le diagnostic plus difficile.