Ce que vous devez savoir en premier
1. Les programmeurs C/C++ gèrent eux-mêmes la mémoire, tandis que la mémoire Java est automatiquement récupérée par le GC.
Bien que je ne sois pas très familier avec le C++, je n'ai probablement pas commis d'erreur de bon sens.
2. Qu’est-ce qu’une fuite de mémoire ?
Une fuite de mémoire fait référence à l'existence de mémoire dans le système qui ne peut pas être recyclée, provoquant parfois une mémoire insuffisante ou un crash du système.
En C/C++, des fuites de mémoire se produisent lorsque la mémoire allouée n'est pas libérée.
3. Il y a une fuite de mémoire dans Java. Nous devons d'abord l'admettre avant de pouvoir continuer à en discuter. Bien que Java présente des fuites de mémoire, vous n'avez fondamentalement pas besoin de vous en soucier, en particulier ceux qui ne se soucient pas du code lui-même.
Les fuites de mémoire en Java signifient certainement : il existe des objets inutiles qui ne peuvent pas être recyclés par le ramasse-miettes.
Et même s’il y a un problème de fuite de mémoire, il se peut qu’il n’apparaisse pas.
4. Les paramètres en Java sont tous transmis par valeur.
Il n’y a fondamentalement aucune objection aux types de base, mais nous ne pouvons avoir aucune objection aux types de référence.
Fuites de mémoire Java
1. Débordement de mémoire du tas (outOfMemoryError : espace du tas Java)
Dans la spécification JVM, la mémoire du tas est utilisée pour générer des instances d'objets et des tableaux.
Si elle est subdivisée, la mémoire tas peut également être divisée en la jeune génération et l'ancienne génération. La jeune génération comprend une zone édénique et deux zones survivantes.
Lorsqu'un nouvel objet est généré, le processus d'application de mémoire est le suivant :
a. Le JVM essaie d'abord d'allouer la mémoire requise pour le nouvel objet dans la zone Eden ;
b. Si la taille de la mémoire est suffisante, l'application se termine, sinon l'étape suivante est :
c. JVM démarre youngGC et essaie de libérer les objets inactifs dans la zone Eden. Après la libération, si l'espace Eden n'est toujours pas suffisant pour placer de nouveaux objets, elle essaie de placer certains des objets actifs dans Eden dans la zone Survivant ;
d. La zone Survivant est utilisée comme zone d'échange intermédiaire entre Eden et l'ancien. Lorsque la zone ANCIENNE dispose de suffisamment d'espace, les objets de la zone Survivant seront déplacés vers la zone Ancien, sinon ils seront conservés dans la zone Survivant ;
e. Lorsqu'il n'y a pas assez d'espace dans la zone OLD, la JVM effectuera un GC complet dans la zone OLD ;
f. Après un GC complet, si les zones Survivor et OLD ne peuvent toujours pas stocker certains objets copiés depuis Eden, empêchant la JVM de créer une zone mémoire pour les nouveaux objets dans la zone Eden, une « erreur de mémoire insuffisante » apparaîtra :
outOfMemoryError:espace de tas Java
2. Débordement de mémoire dans la zone de méthode (outOfMemoryError : espace permgem)
Dans la spécification JVM, la zone méthode stocke principalement les informations de classe, les constantes, les variables statiques, etc.
Par conséquent, si le programme charge trop de classes ou utilise une technologie de génération de proxy dynamique telle que la réflexion ou gclib, cela peut provoquer un dépassement de mémoire dans cette zone. Généralement, le message d'erreur lorsqu'un dépassement de mémoire se produit dans cette zone est :
outOfMemoryError:permgem espace
3. Débordement de pile de threads (java.lang.StackOverflowError)
La pile de threads est une structure de mémoire unique au thread, donc les problèmes avec la pile de threads doivent être des erreurs générées lors de l'exécution d'un thread.
Généralement, le débordement de pile de threads est causé par une récursion trop profonde ou par un trop grand nombre de niveaux d'appels de méthode.
Le message d'erreur lorsqu'un débordement de pile se produit est :
Java. langue. StackOverflowError
Plusieurs scénarios de fuites de mémoire :
1. Les objets à longue durée de vie contiennent des références à des objets à courte durée de vie.
Il s’agit du scénario le plus courant de fuites de mémoire et d’un problème courant dans la conception de code.
Par exemple : si des variables locales sont mises en cache dans une carte statique globale et qu'il n'y a pas d'opération d'effacement, la carte deviendra de plus en plus grande au fil du temps, provoquant des fuites de mémoire.
2. Modifiez la valeur du paramètre de l'objet dans le hashset, et le paramètre est le champ utilisé pour calculer la valeur de hachage.
Une fois qu'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 la valeur de hachage lorsqu'il était initialement stocké 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, elle renverra le résultat que l'objet est introuvable, ce qui entraînera également l'échec de la recherche. supprimez l'objet actuel de la collection HashSet, provoquant une fuite de mémoire.
3. Définissez le nombre de connexions et l'heure d'arrêt de la machine
Ouvrir une connexion très gourmande en ressources pendant une longue période peut également provoquer des fuites de mémoire.
Regardons un exemple de fuite de mémoire :
public class Stack { private Object[] elements=new Object[10]; private int size = 0; public void push(Object e){ EnsureCapacity(); size == 0) throw new EmptyStackException(); return elements[--size]; private void EnsureCapacity(){ if(elements.length == size){ Object[] oldElements = éléments ; éléments = nouvel objet[2 * éléments. longueur+1] ; Système. arraycopy(oldElements,0, éléments, 0, taille);
Le principe ci-dessus devrait être très simple. Si 10 éléments sont ajoutés à la pile puis tous sont retirés, même si la pile est vide et que nous ne voulons rien, cet objet ne peut pas être recyclé. Cela répond aux deux exigences des fuites de mémoire. Condition : Inutile, ne peut pas être recyclé.
Mais même l’existence d’une telle chose n’entraîne pas nécessairement de conséquences si cette pile est moins utilisée.
C'est juste un gaspillage de quelques K de mémoire. De toute façon, notre mémoire est déjà à la hauteur de G, alors quel impact cela aura-t-il d'ailleurs, cette chose sera bientôt recyclée, alors qu'importe ? Regardons deux exemples ci-dessous.
Exemple 1
classe publique Mauvais{ public static Stack s=Stack(); static{ s. push(nouvel objet()); s. pop(); //Il y a une fuite de mémoire dans un objet ici. push(new Object()); //L'objet ci-dessus peut être recyclé, ce qui équivaut à une auto-réparation}}
Parce qu'il est statique, il existera jusqu'à la fin du programme, mais nous pouvons également voir qu'il a une fonction d'auto-guérison.
C'est-à-dire que si votre Stack contient au plus 100 objets, alors seulement 100 objets au maximum ne peuvent pas être recyclés. En fait, cela devrait être facile à comprendre. Stack contient 100 références en interne. Le pire des cas est qu'elles sont toutes inutiles. , car Une fois qu'on aura mis de nouveaux progrès, les références précédentes disparaîtront naturellement !
Exemple 2
public class NotTooBad{ public void doSomething(){ Stack s=new Stack(); push(new Object()); //autres codes. pop();//Cela entraîne également l'impossibilité de recycler l'objet et une fuite de mémoire. }//Quittez la méthode, s est automatiquement invalide, s peut être recyclé et les références à l'intérieur de la Stack ont naturellement disparu, donc // L'auto-guérison peut également être effectuée ici, et on peut dire que cette méthode n'a pas un problème de fuite de mémoire, mais il sera remis plus tard// Il n'est donné qu'à GC, car il est fermé et non ouvert au monde extérieur. Vous pouvez dire ce qui précède. Code 99. // Les situations n'auront aucun impact sur 9999 %. Bien sûr, si vous écrivez un tel code, cela n'aura pas d'effets négatifs, mais // on peut certainement dire que c'est du code poubelle ! Je vais en ajouter une. Une boucle for vide n'aura pas d'impact majeur, n'est-ce pas ?
Les deux exemples ci-dessus sont tout simplement triviaux, mais les fuites de mémoire en C/C++ ne sont pas mauvaises, mais pires.
S'ils ne sont pas recyclés au même endroit, ils ne pourront jamais être recyclés. Si vous appelez fréquemment cette méthode, la mémoire sera utilisée !
Étant donné que Java possède également une fonction d'auto-réparation (je l'ai nommée moi-même et je n'ai pas encore déposé de brevet), le problème de fuite de mémoire de Java peut être presque ignoré, mais ceux qui le connaissent ne devraient pas le commettre.
Afin d'éviter les fuites de mémoire, vous pouvez vous référer aux suggestions suivantes lors de l'écriture du code :
1. Libérer le plus tôt possible les références aux objets inutiles ;
2. Utilisez le traitement des chaînes, évitez d'utiliser String et utilisez largement StringBuffer. Chaque objet String doit occuper une zone de mémoire indépendante ;
3. Utilisez le moins possible les variables statiques, car les variables statiques sont stockées dans la génération permanente (zone de méthode) et la génération permanente ne participe fondamentalement pas au garbage collection ;
4. Évitez de créer des objets en boucles ;
5. L'ouverture de fichiers volumineux ou la suppression simultanée de trop de données de la base de données peuvent facilement provoquer un débordement de mémoire. Par conséquent, à ces endroits, vous devez calculer approximativement la quantité maximale de données et définir les valeurs d'espace mémoire minimales et maximales requises.