web-dev-qa-db-fra.com

Peut-il y avoir une fuite de mémoire en Java

Je reçois cette question à plusieurs reprises. Quel est un bon moyen de répondre

33
javaguy

Peut-il y avoir une fuite de mémoire en Java?

La réponse est que cela dépend du type de fuite de mémoire dont vous parlez.

Des fuites de mémoire classiques en C/C++ se produisent lorsqu'une application néglige de free ou dispose un objet lorsqu'elle est terminée et qu'il fuit. Les références cycliques sont un sous-cas de ce cas où l'application a des difficultés à savoir quand free/dispose et omet de le faire en conséquence. Les problèmes connexes sont ceux où l'application utilise un objet après sa libération ou tente de le libérer deux fois. (Vous pouvez appeler ces derniers problèmes des fuites de mémoire, ou simplement des bugs. Dans les deux cas ...)

Java et autres (entièrement1) langages gérés la plupart du temps ne souffrez pas de ces problèmes, car le GC prend soin de libérer les objets inaccessibles. (Il n’ya certainement pas de problème de pointeur ni de double libre), et les cycles ne sont pas problématiques comme ils le sont pour les "pointeurs intelligents" C/C++ et autres schémas de comptage de références.

Cependant, dans certains cas, GC dans Java manquera d’objets qui (du point de vue du programmeur) doivent être nettoyés. Cela se produit lorsque le GC ne peut pas comprendre qu'un objet ne peut pas être atteint:

  • La logique/l'état du programme peut être tel que les chemins d'exécution qui utiliseraient une variable ne peuvent pas se produire. Le développeur peut voir cela comme une évidence, mais le GC ne peut en être sûr et fait preuve de prudence (comme il est tenu de le faire).
  • Le programmeur pourrait se tromper à ce sujet et le GC évite ce qui pourrait sinon entraîner une référence en suspens.

(Notez que les causes des fuites de mémoire en Java peuvent être simples ou assez subtiles; voir la réponse de @ jonathan.cone pour des réponses subtiles. La dernière implique potentiellement des ressources externes que vous ne devriez pas compter sur le GC doit faire face quand même.)

Dans les deux cas, vous pouvez rencontrer des situations dans lesquelles des objets indésirables ne peuvent pas être récupérés et traînent en mémoire, ce qui entraîne une perte de mémoire ... une fuite de mémoire.

Il se pose également le problème suivant: une application ou une bibliothèque Java peut allouer des objets hors tas via un code natif qui doit être géré manuellement. Si l'application/bibliothèque est défectueuse ou est utilisée de manière incorrecte, vous pouvez obtenir une fuite de mémoire native. (Par exemple: Fuite de mémoire Android Bitmap ..., notant que ce problème est résolu dans les versions ultérieures d'Android.)


1 - Je fais allusion à quelques choses. Certains langages gérés vous permettent d'écrire du code non géré dans lequel vous pouvez créer des fuites de stockage classiques. Certains autres langages gérés (ou plus précisément des implémentations de langages) utilisent le comptage de références plutôt que la collecte des déchets appropriée. Un gestionnaire de stockage basé sur le nombre de références a besoin de quelque chose (c'est-à-dire l'application) pour interrompre les cycles ... sinon des fuites de stockage s'ensuivront.

21
Stephen C

Eh bien, étant donné que Java utilise un ramasse-miettes pour collecter les objets inutilisés, vous ne pouvez pas avoir de pointeur en suspens. Cependant, vous pouvez conserver un objet dans la portée plus longtemps que nécessaire, ce qui pourrait être considéré comme une fuite de mémoire. Plus d'informations ici: http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/0816_GuptaPalanki/

Faites-vous un test sur ceci ou quelque chose? Parce que c'est au moins un A + ici.

10
AnthonyJoeseph

Oui. Des fuites de mémoire peuvent toujours se produire même lorsque vous utilisez un CPG. Par exemple, vous pouvez conserver des ressources telles que les jeux de résultats de la base de données que vous devez fermer manuellement.

8
Markus Johnsson

La réponse est un retentissant yes , mais ceci est généralement le résultat du modèle de programmation plutôt que l’indication d’un défaut de la machine virtuelle Java. Ceci est courant lorsque les structures ont des cycles de vie différents de ceux d'une machine virtuelle Java en cours d'exécution. Quelques exemples sont:

  1. Recharger un contexte
  2. Omettre de déréférencer les observateurs (auditeurs)
  3. Oublier de nettoyer les ressources une fois que vous avez fini de les utiliser *

* - Des milliards de dollars de consultation ont été réalisés pour résoudre le dernier

5
jonathan.cone

Oui, dans la mesure où votre application Java peut accumuler de la mémoire avec le temps que le récupérateur de mémoire ne parvient pas à libérer.

En conservant des références à des objets non supprimés/indésirables, ils ne tomberont jamais hors de portée et leur mémoire ne sera pas réclamée.

4
Tyler

oui, si vous ne déréposez pas les objets, ils ne seront jamais mis au rebut et l'utilisation de la mémoire augmentera. Cependant, en raison de la conception de Java, cela est difficile à atteindre, alors que dans d'autres langues, il est parfois difficile de ne pas y parvenir.

edit: lisez le lien d'Amokrane. c'est bon.

3
pstanton

Le livre Effective Java donne deux autres raisons pour les "fuites de mémoire":

  • Une fois que vous avez mis la référence de l'objet dans Cache et que vous avez oublié qu'elle est là. La référence reste dans le cache longtemps avant de perdre sa pertinence. La solution consiste à représenter le cache en tant que WeakHashMap
  • dans une API où les clients enregistrent des rappels et ne les réinscrivent pas explicitement. La solution consiste à ne stocker que des références faibles à celles-ci.
2
Trivikram

La réponse courte:

Une machine virtuelle Java compétente n'a aucune fuite de mémoire, mais vous pouvez utiliser plus de mémoire que nécessaire, car tous les objets inutilisés n'ont pas encore été nettoyés. En outre, les applications Java peuvent elles-mêmes contenir des références aux objets dont elles n'ont plus besoin, ce qui peut entraîner une fuite de mémoire.

Une réponse encore plus courte:

Java est un langage et ne peut pas avoir de fuites de mémoire, seules les machines virtuelles Java peuvent le faire.

2
Michael Goldshteyn

Oui c'est possible.

Dans Effective Java, il existe un exemple impliquant une pile mise en œuvre à l'aide de tableaux. Si vos opérations pop décrémentent simplement la valeur d'index, il est possible qu'il y ait une fuite de mémoire. Pourquoi? Parce que votre tableau a toujours une référence à la valeur sautée et que vous avez toujours une référence à l’objet pile. Donc, la bonne chose à faire pour cette mise en œuvre de pile serait d'effacer la référence à la valeur sautée en utilisant une affectation null explicite à l'index du tableau éclaté.

2
dteoh

Oui, cela peut arriver, dans un contexte lorsqu'un programme contient par erreur une référence à un objet qui ne serait plus jamais utilisé et par conséquent, il n'est pas nettoyé par le GC.

Un exemple serait d'oublier de fermer un flux ouvert:

class MemoryLeak {

    private void startLeaking() throws IOException {
        StringBuilder input = new StringBuilder();
        URLConnection conn = new URL("www.example.com/file.txt").openConnection();

        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));        

        while (br.readLine() != null) {
            input.append(br.readLine());
        }
    }

    public static void main(String[] args) throws IOException {
        MemoryLeak ml = new MemoryLeak();
        ml.startLeaking();
    }
}
1
Stas

Une réponse simple est la suivante: JVM s’occupe de toute votre initialisation des [objets Java classiques] de POJO tant que vous ne travaillez pas avec JNI. Avec JNI, si vous avez alloué de la mémoire avec le code natif, vous devez vous en occuper vous-même.

0
Swaranga Sarma

Oui. Une fuite de mémoire est une mémoire inutilisée non libérée dans le gestionnaire de mémoire par l'application.

J'ai souvent vu du code Java qui stocke des éléments dans une structure de données, mais ceux-ci ne sont jamais supprimés, remplissant la mémoire jusqu'à une erreur OutOfMemoryError:

void f() {
    List<Integer> w = new ArrayList<Integer>();
    while (true) {
         w.add(new Integer(42));
    }
}

Bien que cet exemple soit trop évident, les erreurs de mémoire Java ont tendance à être plus subtiles. Par exemple, utiliser Dependency Injection pour stocker un objet énorme sur un composant avec SESSION scope , sans le relâcher lorsque l’objet n’est plus utilisé.

Sur un VM 64 bits _, cela a tendance à s’aggraver car l’espace mémoire de swap commence à se remplir jusqu’à ce que le système analyse trop d’opérations IO.

0
vz0