Maison  >  Article  >  Java  >  Analyse des cas de problèmes de fuite de mémoire Java

Analyse des cas de problèmes de fuite de mémoire Java

WBOY
WBOYavant
2023-05-23 18:46:06964parcourir

Problème de fuite de mémoire Java

La soi-disant fuite de mémoire signifie qu'un objet ou une variable qui n'est plus utilisé par le programme a été occupé dans la mémoire.

Il existe un mécanisme de garbage collection en Java, qui peut garantir que lorsqu'un objet n'est plus référencé, c'est-à-dire lorsque l'objet devient orphelin, l'objet sera automatiquement effacé de la mémoire par le garbage collector.

Puisque Java dispose d'un mécanisme de récupération de place, pourquoi y a-t-il toujours un problème de fuite de mémoire ?

Rien de plus que certains objets ne peuvent pas être traités par le garbage collector, ce qui fait que ces objets occupent tout le temps la mémoire JVM, ce qui entraînera des fuites de mémoire.

Étant donné que Java utilise un graphe orienté pour la gestion du garbage collection, il peut éliminer le problème des cycles de référence. Par exemple, s'il y a deux objets qui se référencent l'un l'autre, tant qu'ils sont inaccessibles au processus racine, le GC peut le faire. recyclez-les également, par exemple. Le code suivant peut voir la récupération de mémoire dans ce cas.

import java. io.IOException;
public class GarbageTest {

    public static void main(String[] args) throws IOException {
        try {
            // TODO Auto-generated method stub
            gcTest();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("has exited gcTest!");
        System.in.read();
        System.in.read();
        System.out.println("out begin gc!");
        for (int i = 0; i < 100; i++) {
            System.gc();
            System.in.read();
            System.in.read();
        }
    }

    private static void gcTest() throws IOException {
        System.in.read();
        System.in.read();
        Person p1 = new Person();
        System.in.read();
        System.in.read();
        Person p2 = new Person();
        p1.setMate(p2);
        p2.setMate(p1);
        System.out.println("before exit gctest!");
        System.in.read();
        System.in.read();
        System.gc();
        System.out.println("exit gctest!");
    }

    private static class Person {
        byte[] data = new byte[20000000];
        Person mate = null;

        public void setMate(Person other) {
            mate = other;
        }
    }

}

Fuites de mémoire en Java : des fuites de mémoire sont susceptibles de se produire lorsque des objets à durée de vie longue contiennent des références à des objets à durée de vie courte. Bien que les objets à durée de vie courte ne soient plus nécessaires, car les objets à durée de vie longue contiennent des références. il ne peut pas être recyclé. C'est le scénario dans lequel des fuites de mémoire se produisent en Java. En termes simples, le programmeur peut créer un objet et ne plus jamais l'utiliser, mais l'objet est toujours référencé, c'est-à-dire que ces objets sont inutiles mais ne peuvent pas être recyclés. par le garbage collector. Il s'agit d'une situation dans laquelle des fuites de mémoire peuvent se produire en Java.

Par exemple, dans le système de cache, nous chargeons un objet et le mettons dans le cache (par exemple, dans un objet cartographique global), puis ne l'utilisons plus jamais. La valeur de cet objet est référencée par le cache, mais. il n'est plus utilisé.

Pour vérifier les fuites de mémoire en Java, vous devez laisser le programme exécuter toutes les branches jusqu'à la fin du programme, puis vérifier si un objet a été utilisé. Sinon, vous pouvez déterminer que l'objet est une fuite de mémoire.

Si la méthode d'un objet instance d'une classe externe renvoie un objet instance d'une classe interne, l'objet classe interne est référencé pendant longtemps, même si l'objet instance de classe externe n'est plus utilisé, mais parce que la classe interne persiste l'objet d'instance de la classe externe, cet objet de classe externe ne sera pas récupéré, ce qui entraînera également des fuites de mémoire.

Le contenu suivant provient d'Internet (la fonctionnalité principale est d'effacer un élément de la pile, non pas de le supprimer complètement du tableau, mais de réduire la quantité totale de stockage. Je peux écrire mieux que cela. Après avoir supprimé un element Lorsqu'il y a un élément, il disparaîtra du tableau d'ailleurs, et la valeur de la position de cet élément peut être définie sur null)

Je ne peux vraiment pas penser à un exemple plus classique que cette pile, donc à tel point que je dois citer les exemples d'autres personnes, les exemples suivants ne sont pas ceux auxquels je pensais, ils étaient ceux que j'ai vus dans le livre. Bien sûr, si je ne les avais pas vus dans le livre, j'y aurais peut-être pensé. moi-même au bout d'un moment. Mais à ce moment-là, personne ne m'a cru quand j'ai dit que j'y avais pensé moi-même.

public class Stack {
    private Object[] elements = new Object[10];
    private int size = 0;

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0) throw new EmptyStackException();
        return elements[--size];
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            Object[] oldElements = elements;
            elements = new Object[2 * elements.length + 1];
            System.arraycopy(oldElements, 0, elements, 0, size);
        }
    }
}

Le principe ci-dessus devrait être très simple. Si 10 éléments sont ajoutés à la pile d'argent, puis qu'ils apparaissent tous, même si la pile d'argent est vide et que nous ne voulons rien, c'est un objet qui ne peut pas être recyclé, donc c'est cohérent. Les deux conditions de fuite de mémoire sont inutiles et ne peuvent pas être recyclées. Mais même l'existence d'une telle chose n'entraîne pas nécessairement de conséquences. Si cette pile d'argent est moins utilisée, cela ne gaspillera que quelques K de mémoire. Quoi qu'il en soit, notre mémoire est déjà supérieure à G, alors quel impact cela aura-t-il. ? , En plus, ce truc sera bientôt recyclé, alors qu'importe ? Regardons deux exemples ci-dessous.

class Bad {
    public static Stack s = new Stack();
    static {
        s.push(new Object());

        s.pop(); //这里有一个对象发生内存泄露

        s.push(new Object());//上面的对象可以被回收了,等于是自愈了
    }
}

Parce qu'il est statique, il existera jusqu'à la fin du programme, mais on peut aussi voir qu'il a une fonction d'auto-réparation, c'est-à-dire que si votre Stack a un maximum de 100 objets, alors seulement un maximum de. 100 objets ne peuvent pas être recyclés. En fait, cela devrait être facile à comprendre. Stack contient 100 références en interne. Dans le pire des cas, elles sont toutes inutiles, car une fois que nous en ajoutons de nouvelles, les références précédentes disparaîtront naturellement !

Autre situation de fuite de mémoire : Lorsqu'un objet est stocké dans la collection HashSet, les champs de l'objet qui participent au calcul de la valeur de hachage ne peuvent pas être modifiés, sinon la valeur de hachage modifiée de l'objet sera différente de l'original stocké. valeur. La valeur de hachage est différente lorsqu'elle est placée dans la collection HashSet. Dans ce cas, même si la méthode contain utilise la référence actuelle de l'objet comme paramètre pour récupérer l'objet de la collection HashSet, le résultat est que l'objet ne peut pas. être trouvé sera renvoyé. Cela rendra également impossible la suppression individuelle de l'objet actuel de la collection HashSet, provoquant des fuites de mémoire.

Pièce jointe : situations typiques de fuites de mémoire

(1) Le problème de fuite de mémoire à court terme causé par la structure des données, regardez le code ci-dessous

public class Stack{  
      private Object[] element=new Object[10];  
      private int size=0;  
        
      public void push(Object ele){  
             ensureCapacity();  
             element[size++]=ele;  
      }  
  
      public Object pop(){  
             if(size==0) throw new EmptyStackException();  
             return element[--size]; //短暂造成内存泄露  
      }  
  
      private void ensureCapacity(){  
             if(element.length==size){  
                     Object[] oldElement=element;  
                     element=new Object[size*2+1];  
                     System.arraycopy(oldElement,0,element,0,size);  
             }  
      }  
}

Chaque fois que pop() est utilisé dans le code ci-dessus, Stack apparaîtra un élément. S'il n'est pas ajouté, avant le nouvel élément, il y a en fait toujours un élément de référence [x] pointant vers l'objet sauté, donc le GC ne le récupérera pas. Ce n'est qu'en définissant element[x]=newObject lors de push() qu'un nouvel élément peut recycler les objets précédemment créés. Il est beaucoup plus sûr de remplacer la méthode pop() ci-dessus par le code suivant :

public Object pop(){  
       if(element.length==size) throws EmptyStackException();  
       Object o=element[--size];  
       elements[size]=null;  //使得GC有机会回收这个对象  
       return o;  
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer