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


Je reçois cette question posée plusieurs fois. Quelle est une bonne façon de répondre

Author: Stephen C, 2011-02-14

12 answers

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.

Les fuites de mémoire C / C++ classiques se produisent lorsqu'une application néglige free ou dispose un objet lorsqu'ils en ont fini avec lui, et il fuit. Les références cycliques en sont un sous-cas où l'application a du mal à savoir quand free / dispose, et néglige de le faire en conséquence. Les problèmes où l'application utilise un objet après il a été libéré, ou tente de le libérer deux fois. (Vous pouvez appeler ces derniers problèmes des fuites de mémoire, ou tout simplement des bugs. De toute façon ... )

Java et autres (entièrement1) les langages gérés la plupart du temps ne souffrent pas de ces problèmes car le GC s'occupe de libérer les objets qui ne sont plus accessibles. (Certainement, les problèmes de pointeur suspendu et de double-libre n'existent pas, et les cycles ne sont pas problématiques comme ils le sont pour les "pointeurs intelligents" C / C++ et autres comptes de référence régime.)

Mais dans certains cas, GC en Java manquera des objets qui (du point de vue du programmeur) devraient être collecté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 évident, mais le GC ne peut pas être sûr, et se trompe du côté de la prudence (comme il est nécessaire).
  • Le le programmeur pourrait se tromper à ce sujet, et le GC évite ce qui pourrait autrement entraîner une référence pendante.

(Notez que les causes des fuites de mémoire en Java peuvent être simples, ou assez subtiles; voir @jonathan.la réponse de cone pour certains subtils. La dernière implique potentiellement des ressources externes que vous ne devriez de toute façon pas compter sur le GC pour gérer.)

De toute façon, vous pouvez avoir une situation où les objets indésirables ne peuvent pas être ramassés et traîner d'encombrer la mémoire ... une fuite de mémoire.

Ensuite, il y a le problème qu'une application ou une bibliothèque Java peut allouer des objets hors tas via du code natif qui doivent être gérés manuellement. Si l'application / bibliothèque est boguée ou est utilisée de manière incorrecte, vous pouvez obtenir une fuite de mémoire native. (Par exemple: Fuite de mémoire Bitmap Android ... notant que ce problème est résolu dans les versions ultérieures d'Android.)


1 - Je fais allusion à quelques choses. Quelques langues gérées vous permet d'écrire du code non géré où vous pouvez créer des fuites de stockage classiques. Certains autres langages gérés (ou plus précisément des implémentations de langage) utilisent le comptage des références plutôt que la collecte de 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.

 20
Author: Stephen C, 2017-12-10 01:11:19

Eh bien, étant donné que java utilise un garbage collector pour collecter les objets inutilisés, vous ne pouvez pas avoir de pointeur suspendu. Cependant, vous pouvez garder un objet dans la portée plus longtemps qu'il ne doit l'être, ce qui pourrait être considéré comme une fuite de mémoire. Plus à ce sujet ici: http://web.archive.org/web/20120722095536/http://www.ibm.com:80/developerworks/rational/library/05/0816_GuptaPalanki/

Tu fais un test sur ça ou quelque chose? Parce que c'est au moins un A+ juste là.

 10
Author: AnthonyJoeseph, 2017-07-25 01:49:38

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

 8
Author: Markus Johnsson, 2011-02-13 22:38:02

La réponse est un ouiretentissant, mais c'est généralement le résultat du modèle de programmation plutôt qu'une indication d'un défaut dans la JVM. Ceci est courant lorsque les frameworks ont des cycles de vie différents de ceux d'une JVM en cours d'exécution. Voici quelques exemples:

  1. le Rechargement d'un contexte
  2. Omission de déréférencer les observateurs (auditeurs)
  3. Oublier de nettoyer les ressources après avoir fini de les utiliser *

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

 5
Author: jonathan.cone, 2011-02-13 22:59:37

Oui, dans le sens où votre application Java peut accumuler de la mémoire au fil du temps que le garbage collector est incapable de libérer.

En conservant les références aux objets non hérités/indésirables, ils ne tomberont jamais hors de portée et leur mémoire ne sera pas récupérée.

 4
Author: Tyler, 2011-02-13 22:37:31

Oui, si vous ne dé-référencez pas les objets, ils ne seront jamais collectés et l'utilisation de la mémoire augmentera. cependant, en raison de la façon dont java est conçu, cela est difficile à réaliser alors que dans d'autres langages, il est parfois difficile de ne pas le faire.

Edit: lire le lien d'Amokrane. que c'est bon.

 3
Author: pstanton, 2011-02-13 22:34:37

Oui c'est possible.

En Java effectif, il existe un exemple impliquant une pile implémentée à l'aide de tableaux. Si vos opérations pop décrémentent simplement la valeur d'index, il est possible d'avoir une fuite de mémoire. Pourquoi? Parce que votre tableau a toujours une référence à la valeur apparue et que vous avez toujours une référence à l'objet stack. La bonne chose à faire pour cette implémentation de pile serait donc d'effacer la référence à la valeur sauté à l'aide d'une affectation nulle explicite au tableau sauté index.

 2
Author: dteoh, 2011-02-13 22:37:12

La réponse courte:

Une JVM compétente n'a pas de mémoire fuites, mais plus de mémoire peut être utilisée ce qui est nécessaire, parce que pas tous inutilisés les objets ont été ramassés, encore. En outre, les applications Java elles-mêmes peuvent contenir des références à des 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 fuites de mémoire, seulement Java Virtuel Les machines peuvent.

 2
Author: Michael Goldshteyn, 2011-02-14 15:08:45

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

  • Une fois que vous mettez la référence d'objet dans Cache et oubliez qu'elle est là. La référence reste dans le cache longtemps avant de devenir non pertinente. La solution consiste à représenter le cache comme un WeakHashMap
  • dans une API où les clients enregistrent des rappels et ne les réenregistrent pas explicitement. La solution est de ne stocker que des références faibles à eux.
 2
Author: Trivikram, 2017-07-25 03:48:44

Une réponse simple est : JVM prendra soin de toute votre initialisation des [vieux objets java simples] de POJO tant que vous ne travaillez pas avec JNI. Avec JNI si vous avez fait une allocation de mémoire avec le code natif, vous devez prendre soin de cette mémoire par vous-même.

 0
Author: Swaranga Sarma, 2011-02-13 22:38:04

Oui. Une fuite de mémoire est la mémoire inutilisée pas publié pour le gestionnaire de mémoire par l'application.

J'ai vu plusieurs fois du code Java qui stocke des éléments sur une structure de données mais les éléments ne sont jamais supprimés de là, remplissant la mémoire jusqu'à une 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, en utilisant l'injection de dépendance, vous stockez un objet énorme sur un composant avec SESSION scope , sans le libérer lorsque l'objet n'est plus utilisé.

Sur une machine virtuelle 64 bits, cela a tendance à s'aggraver car l'espace mémoire d'échange commence à être rempli jusqu'à ce que le système explore trop d'opérations d'E / S.

 0
Author: vz0, 2011-02-13 23:34:13

Oui, cela peut être, dans un contexte où un programme détient par erreur une référence à un objet qui ne serait plus jamais utilisé et donc il n'est pas nettoyé par le GC.

Un exemple à cela, serait 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();
    }
}
 0
Author: Stas, 2018-09-09 08:26:53