Maison > Java > JavaBase > le corps du texte

Que sont les fuites de mémoire et les débordements de mémoire en Java

青灯夜游
Libérer: 2023-01-13 00:40:03
original
15318 Les gens l'ont consulté

Une fuite de mémoire signifie qu'une fois que le programme a demandé de la mémoire, il ne peut pas libérer l'espace mémoire alloué. Un dépassement de mémoire signifie que lorsque le programme demande de la mémoire, il n'y a pas assez de mémoire pour que le demandeur puisse l'utiliser ou qu'il fournit un espace de stockage pour stocker des données int, mais que des données longues sont stockées, le résultat est que la mémoire n'est pas suffisante, et une erreur MOO est signalée. L’accumulation de fuites de mémoire finira par entraîner un débordement de mémoire.

Que sont les fuites de mémoire et les débordements de mémoire en Java

L'environnement d'exploitation de ce tutoriel : système windows7, version java8, ordinateur DELL G3.

1. Fuite de mémoire :

signifie qu'une fois que le programme a demandé de la mémoire, il ne peut pas libérer l'espace mémoire appliqué ne semble pas avoir un grand impact, mais la conséquence des fuites de mémoire accumulées est la mémoire. débordement.

2. Débordement de mémoire :

signifie que lorsque le programme demande de la mémoire, il n'y a pas assez de mémoire pour que le demandeur puisse l'utiliser, ou en d'autres termes, vous disposez d'un espace de stockage pour stocker des données de type int. , mais vous stockez des données de type long, le résultat est que la mémoire n'est pas suffisante et une erreur MOO sera signalée, ce qu'on appelle un débordement de mémoire.

3. La relation entre les deux :

  • L'accumulation de fuites de mémoire finira par entraîner un débordement de mémoire

  • Un débordement de mémoire signifie que l'espace mémoire que vous souhaitez dépasse l'espace qui vous est réellement alloué par le système. À ce stade, le système est silencieux. S'il ne peut pas répondre à vos besoins, une erreur de dépassement de mémoire sera signalée.

  • Une fuite de mémoire signifie que vous demandez au système d'allouer de la mémoire à utiliser (nouvelle), mais que vous ne la restituez pas après utilisation (suppression). Par conséquent, vous ne pouvez plus accéder à la mémoire que vous avez demandée (peut-être que vous). mettez-le L'adresse du programme est perdue), et le système ne peut pas l'attribuer à nouveau au programme requis. Cela équivaut à louer une armoire avec une clé. Après avoir stocké vos affaires et verrouillé l'armoire, vous perdez la clé ou ne la restituez pas. Le résultat est que l'armoire ne peut être utilisée par personne et ne peut pas être jetée par le recycleur. recycle car il ne trouve aucune information le concernant.

  • Débordement de mémoire : Une assiette ne peut contenir que 4 fruits après avoir essayé différentes méthodes. Vous en avez mis 5, mais elle est tombée par terre et n'a pas pu être mangée. C'est un débordement. Par exemple, si une pile est repoussée alors qu'elle est pleine, un débordement d'espace se produira, appelé débordement. Si une pile est repoussée alors qu'elle est vide, un débordement d'espace se produira, appelé débordement inférieur. Autrement dit, la mémoire allouée n’est pas suffisante pour contenir la séquence d’éléments de données, ce que l’on appelle un débordement de mémoire. Pour parler franchement, je ne supporte pas grand-chose, je vais donc signaler une erreur.

4. Classification des fuites de mémoire (classées selon la manière dont elles se produisent)

  • Fuites de mémoire courantes. Le code présentant des fuites de mémoire sera exécuté plusieurs fois, provoquant une fuite de mémoire à chaque exécution.

  • Fuites de mémoire occasionnelles. Le code provoquant des fuites de mémoire ne se produira que dans certaines circonstances ou opérations. Les cas fréquents et sporadiques sont relatifs. Dans certaines circonstances, ce qui est occasionnel peut devenir courant. L’environnement de test et les méthodes de test sont donc cruciaux pour détecter les fuites de mémoire.

  • Fuite de mémoire unique. Le code qui provoque une fuite de mémoire ne sera exécuté qu'une seule fois, ou en raison de défauts algorithmiques, il n'y aura toujours qu'un seul et un seul bloc de mémoire divulgué. Par exemple, si de la mémoire est allouée dans le constructeur d’une classe, mais que la mémoire n’est pas libérée dans le destructeur, la fuite de mémoire ne se produira qu’une seule fois.

  • Fuite de mémoire implicite. Le programme alloue continuellement de la mémoire pendant son exécution, mais ne libère la mémoire qu'à la fin. À proprement parler, il n’y a pas de fuite de mémoire ici, car le programme finit par libérer toute la mémoire demandée. Mais pour un programme serveur qui doit fonctionner pendant des jours, des semaines, voire des mois, le fait de ne pas libérer de mémoire à temps peut également conduire à l'épuisement éventuel de toute la mémoire du système. Par conséquent, nous appelons ce type de fuite de mémoire une fuite de mémoire implicite.

5. Causes et solutions du débordement de mémoire :

(1) Causes du débordement de mémoire :

  • La quantité de données chargées dans la mémoire est trop importante, par exemple, trop de données sont prises. sorti de la base de données en une seule fois ;

  • Il y a des références à des objets dans la classe collection, qui ne sont pas effacées après utilisation, ce qui rend impossible le recyclage par la JVM

  • Il y a une boucle infinie dans le code ; ou la boucle génère trop d'entités d'objet en double ;

  • Le tiers a utilisé un BUG dans le logiciel ;

  • La valeur de la mémoire du paramètre de démarrage est trop petite

(2) Solution au débordement de mémoire :

La première étape consiste à modifier les paramètres de démarrage de la JVM et à augmenter directement la mémoire. (N'oubliez pas d'ajouter les paramètres -Xms et -Xmx.)

La deuxième étape consiste à vérifier le journal des erreurs pour voir s'il y a d'autres exceptions ou erreurs avant l'erreur "OutOfMemory".

La troisième étape consiste à parcourir et analyser le code pour découvrir où un débordement de mémoire peut se produire.

Concentrez-vous sur les points suivants :

  • Vérifiez s'il existe une requête pour obtenir toutes les données en même temps dans la requête de base de données. D'une manière générale, si cent mille enregistrements sont récupérés simultanément dans la mémoire, cela peut provoquer un débordement de mémoire. Ce problème est relativement caché. Avant la mise en ligne, il y avait moins de données dans la base de données et il était moins susceptible de causer des problèmes. Après la mise en ligne, il y avait plus de données dans la base de données et une seule requête pouvait provoquer un débordement de mémoire. Par conséquent, essayez d'utiliser la pagination pour les requêtes de base de données.

  • Vérifiez s'il y a des boucles infinies ou des appels récursifs dans le code.

  • Vérifiez s'il existe une grande boucle qui génère à plusieurs reprises de nouvelles entités d'objet.

  • Vérifiez s'il existe une requête pour obtenir toutes les données à la fois dans la requête de base de données. De manière générale, si cent mille enregistrements sont récupérés simultanément dans la mémoire, cela peut provoquer un débordement de mémoire. Ce problème est relativement caché. Avant la mise en ligne, il y avait moins de données dans la base de données et il était moins susceptible de causer des problèmes. Après la mise en ligne, il y avait plus de données dans la base de données et une seule requête pouvait provoquer un débordement de mémoire. Par conséquent, essayez d'utiliser la pagination pour les requêtes de base de données.

  • Vérifiez si les objets de collection tels que List et MAP ne sont pas effacés après utilisation. Les objets de collection tels que List et MAP auront toujours des références aux objets, ce qui rendra ces objets incapables d'être recyclés par GC.

La quatrième étape, utilisez l'outil d'affichage de la mémoire pour afficher dynamiquement l'utilisation de la mémoire

Modèle de mémoire JVM8

Dix scénarios de débordement de mémoire

L'exécution JVM nécessite d'abord un chargeur de classe ( classLoader ) charge le fichier de bytecode de la classe requise. Après le chargement, il est transmis au moteur d'exécution pour exécution. Pendant le processus d'exécution, une période d'espace est nécessaire pour stocker les données (analogue au processeur et à la mémoire principale). Le processus d'allocation et de libération de cet espace mémoire est la zone de données d'exécution dont nous devons nous soucier. Le dépassement de mémoire se produit lors du chargement du chargeur de classe. Le dépassement de mémoire est divisé en deux catégories : OutOfMemoryError et StackOverflowError. Ce qui suit répertorie 10 situations de dépassement de mémoire et explique comment le dépassement de mémoire se produit à l'aide d'un exemple de code.

1.java heap memory overflow

Lorsque l'exception java.lang.OutOfMemoryError:Java heap space se produit, il s'agit d'un débordement de mémoire tas.

1), Description du problème

  • L'ensemble de mémoire jvm est trop petit, la mémoire requise pour l'objet est trop grande et cette exception sera levée lorsque l'espace est alloué lors de la création de l'objet.

  • Pics de trafic/données, il existe certaines limites au traitement propre de l'application, comme un certain nombre d'utilisateurs ou une certaine quantité de données. Lorsque le nombre d'utilisateurs ou la quantité de données augmente soudainement et dépasse le seuil attendu, les opérations qui s'exécutaient normalement avant l'arrêt de pointe s'arrêteront et déclencheront Java lang.OutOfMemoryError : erreur d'espace de tas Java

2), exemple de code

Compilez le code suivant et définissez les paramètres jvm sur -Xms20m -Xmx20m lors de l'exécution

Dans l'exemple ci-dessus, si une requête n'alloue qu'une seule fois 5 m de mémoire, la quantité de la requête est très faible et les déchets La collecte sera normale et il n'y aura pas d'erreurs, mais une fois qu'elle sera concurrente, la valeur maximale de la mémoire sera dépassée et un débordement de mémoire sera généré.

3. Solution

Tout d'abord, s'il n'y a aucun problème avec le code, vous pouvez ajuster de manière appropriée les deux paramètres jvm -Xms et -Xmx, et utiliser des tests de résistance pour ajuster ces deux paramètres afin d'obtenir des valeurs optimales.

Deuxièmement, essayez d'éviter les applications pour des objets volumineux, tels que le téléchargement de fichiers et l'obtention de gros lots à partir de la base de données. Essayez d'éviter de les traiter par morceaux ou par lots, ce qui contribuera à l'exécution normale et stable du système. .

Enfin, essayez d'augmenter la vitesse d'exécution d'une requête. Plus tôt le garbage collection est effectué, mieux c'est, sinon, lorsqu'un grand nombre de requêtes simultanées arrivent, la mémoire ne peut pas être allouée à de nouvelles requêtes, ce qui provoquera facilement une avalanche de requêtes. le système.

2. Fuite de mémoire du tas Java

1), description du problème

Les fuites de mémoire en Java sont des situations dans lesquelles certains objets ne sont plus utilisés par l'application mais ne sont pas reconnus par le garbage collection. Par conséquent, ces objets inutilisés existent toujours indéfiniment dans l’espace du tas Java. L'accumulation constante finira par déclencher java.lang.OutOfMemoryError.

2), exemple de code

Lors de l'exécution du code ci-dessus, on pourrait s'attendre à ce qu'il s'exécute indéfiniment sans aucun problème, en supposant que la solution de mise en cache pure n'étend la carte sous-jacente qu'à 10 000 éléments, toutes les clés ne sont pas toutes déjà dans le Carte de hachage. Cependant, en fait, les éléments continueront à être ajoutés car la classe clé ne remplace pas sa méthode equals().

Au fil du temps, comme le code divulgué est constamment utilisé, les résultats « mis en cache » finissent par consommer beaucoup d'espace de mémoire Java. Lorsque la mémoire perdue remplit toute la mémoire disponible dans la zone de tas, le garbage collection ne peut pas la nettoyer, java.lang.OutOfMemoryError.

3), solution

La solution correspondante est relativement simple : il suffit de réécrire la méthode égale :

3. Dépassement de mémoire du délai d'expiration du garbage collection

1), description du problème lorsque l'application épuise toute la mémoire disponible, la limite de surcharge du GC est dépassée. l'erreur et que le GC n'a pas réussi à l'effacer plusieurs fois, une java.lang.OutOfMemoryError sera générée. Lorsque la JVM passe beaucoup de temps à exécuter GC, avec peu d'effet, et une fois que l'ensemble du processus GC dépasse la limite, une erreur sera déclenchée (le temps GC de configuration JVM par défaut dépasse 98 % et la mémoire de tas recyclée est inférieure à 2). %).

2), exemple de code

3), solution

Pour réduire le cycle de vie de l'objet, essayez d'effectuer le garbage collection le plus rapidement possible.

4. Débordement de mémoire métaespace

1), description du problème

Débordement de métaespace, le système lancera java.lang.OutOfMemoryError: Metaspace. La raison de ce problème anormal est que le système contient beaucoup de code ou fait référence à de nombreux packages tiers, ou que la génération de code dynamique et le chargement de classes sont utilisés, ce qui entraîne une empreinte mémoire importante dans le métaespace.

2), Exemple de code

3), Solution

Par défaut, la taille du métaespace n'est limitée que par la mémoire locale. Cependant, pour le bien des performances de l'ensemble de la machine, cet élément doit être réglé autant que possible pour éviter d'arrêter le service de l'ensemble de la machine.

  • Optimiser la configuration des paramètres pour éviter d'affecter les autres processus JVM

-XX : MetaspaceSize, taille de l'espace initiale Lorsque cette valeur est atteinte, le garbage collection sera déclenché pour le déchargement du type, et le GC ajustera la valeur : s'il est libéré. Si une grande quantité d'espace est libérée, réduisez la valeur de manière appropriée ; si une petite quantité d'espace est libérée, augmentez la valeur de manière appropriée lorsqu'elle ne dépasse pas MaxMetaspaceSize.

-XX:MaxMetaspaceSize, l'espace maximum, il n'y a pas de limite par défaut.

En plus des deux options de spécification de taille ci-dessus, il existe deux attributs liés au GC : -XX:MinMetaspaceFreeRatio, après GC, le pourcentage de la capacité minimale de l'espace restant du Metaspace est réduit au garbage collection provoqué par l'espace alloué. -XX:MaxMetaspaceFreeRatio, après GC, le pourcentage de la capacité d'espace restante maximale de Metaspace est réduit au garbage collection provoqué par la libération d'espace.

  • Attention lors de la citation de packages tiers

Pour les packages tiers, assurez-vous de choisir avec soin et de supprimer les packages inutiles. Cela contribuera non seulement à améliorer la vitesse de compilation et de packaging, mais également à améliorer la vitesse de déploiement à distance.

  • Faites attention aux frameworks qui utilisent des classes générées dynamiquement

Pour les frameworks qui utilisent un grand nombre de classes générées dynamiquement, des tests de résistance doivent être effectués pour vérifier si les classes générées dynamiquement dépassent les besoins en mémoire et une exception sera levée .

5. Débordement de mémoire direct

1), description du problème

Il sera utilisé lors de l'utilisation de allocateDirect() dans ByteBuffer De nombreux frameworks javaNIO (comme netty) sont encapsulés comme d'autres méthodes, un java.lang. OutOfMemoryError : une exception de mémoire tampon directe sera levée lorsque ce problème se produit.

Des problèmes similaires se produiront si vous utilisez directement ou indirectement la méthode allocateDirect dans ByteBuffer sans l'effacer.

2), exemple de code

3), solution

Si vous effectuez souvent des opérations similaires, vous pouvez envisager de définir les paramètres : -XX:MaxDirectMemorySize et d'effacer la mémoire à temps.

6. Débordement de mémoire de pile

1), description du problème

Lorsqu'un thread exécute une méthode Java, la JVM crée un nouveau cadre de pile et le pousse en haut de la pile. À ce stade, le nouveau cadre de pile devient le cadre de pile actuel. Lorsque la méthode est exécutée, le cadre de pile est utilisé pour stocker les paramètres, les variables locales, les instructions intermédiaires et d'autres données.

Lorsqu'une méthode s'appelle de manière récursive, les données générées par la nouvelle méthode (qui peut également être comprise comme un nouveau cadre de pile) seront poussées vers le haut de la pile. Chaque fois que la méthode s'appellera, elle copiera les données. de la méthode actuelle et poussez-la vers la pile. Par conséquent, chaque niveau de récursivité nécessite la création d’un nouveau stack frame. Le résultat est que de plus en plus de mémoire dans la pile sera consommée par les appels récursifs. Si les appels récursifs s'appellent un million de fois, un million de trames de pile seront générées. Cela entraînera un débordement de la mémoire de la pile.

2), exemple de code

3), solution

S'il y a effectivement des appels récursifs dans le programme et qu'un débordement de pile se produit, vous pouvez augmenter la taille -Xss pour résoudre le problème de débordement de mémoire de pile. Les appels récursifs empêchent la formation d'une boucle infinie, sinon un débordement de la mémoire de la pile se produira.

7. Débordement de mémoire lors de la création d'un thread local

1), description du problème

Le thread n'occupe essentiellement que la zone mémoire autre que le tas. tas, une zone mémoire ne peut pas être allouée au thread, soit parce que la mémoire elle-même n'est pas suffisante, soit parce que l'espace du tas est trop grand, ce qui ne laisse pas beaucoup de mémoire restante et parce que le thread lui-même occupe de la mémoire. n'est pas suffisant.

2), exemple de code

3), solution

Vérifiez d'abord si le système d'exploitation a une limite sur le nombre de threads qui ne peuvent pas être créés à l'aide du shell. Si tel est le problème, vous devez le faire. ajustez le nombre maximum pris en charge par le système.

Dans le développement quotidien, essayez de vous assurer que le nombre maximum de threads est contrôlable et n'utilisez pas le pool de threads de manière arbitraire. Il ne peut pas croître sans limite.

8. Débordement de mémoire au-delà de la zone d'échange

1), description du problème

Pendant le processus de démarrage des applications Java, vous pouvez limiter la mémoire requise spécifiée via -Xmx et d'autres paramètres de démarrage similaires. Lorsque la mémoire totale demandée par la JVM est supérieure à la mémoire physique disponible, le système d'exploitation commence à convertir le contenu de la mémoire vers le disque dur.

De manière générale, la JVM génère une erreur d'espace d'échange insuffisant, ce qui signifie que lorsque l'application ne parvient pas à demander l'allocation de mémoire au tas natif de la JVM et que le tas natif est sur le point d'être épuisé, le message d'erreur contient la taille du tas natif de la JVM. échec d'allocation (en octets) et raison pour laquelle la demande a échoué.

2) Solution

Augmentez la taille de la zone de swap du système. Je pense personnellement que si la zone de swap est utilisée, les performances seront considérablement réduites. Dans l'environnement de production, essayez d'éviter au maximum. mémoire dépassant la mémoire physique du système. Deuxièmement, supprimez la zone d'échange système et utilisez uniquement la mémoire système pour garantir les performances de l'application.

9. Dépassement de mémoire au-delà de la limite du tableau

1). Description du problème Parfois, vous rencontrerez la description de ce type de dépassement de mémoire. La taille du tableau demandé dépasse la limite de la VM. . Il existe une limite à la taille maximale, qui varie selon la plate-forme, mais se situe généralement entre 1 et 2,1 milliards d'éléments. Lorsque l'erreur La taille du tableau demandé dépasse la limite de la VM se produit, cela signifie que l'application tente d'allouer un tableau plus grand que ce que la machine virtuelle Java peut prendre en charge. Avant que la JVM n'alloue de la mémoire à un tableau, elle effectue une vérification spécifique à la plate-forme : si la structure de données allouée est adressable sur cette plate-forme.

2), exemple de code

Ce qui suit est le code Le tableau dépasse la limite maximale.

3), solution

Par conséquent, la longueur du tableau doit être comprise dans la plage de longueur autorisée par la plateforme. Cependant, cette erreur est généralement rare, principalement parce que l'index d'un tableau Java est de type int. Le plus grand entier positif en Java est 2 ^ 31 - 1 = 2 147 483 647. Et les limites spécifiques à la plate-forme peuvent être très proches de ce nombre, par exemple : sur mon environnement (MacOS 64 bits, exécutant Jdk1.8), je peux initialiser des tableaux avec des longueurs allant jusqu'à 2 147 483 645 (Integer.MAX_VALUE-2). Si la longueur du tableau est augmentée de 1 pour atteindre nteger.MAX_VALUE-1, une OutOfMemoryError se produira.

10. Le système tue le débordement de mémoire du processus

1) Présentation du problème Avant de décrire ce problème, familiarisez-vous d'abord avec certaines connaissances du système d'exploitation : le système d'exploitation est construit sur le concept de processus, et ces processus sont dans le noyau Dans l'opération, il existe un processus très spécial appelé "Out of memory killer". Lorsque le noyau détecte que le système ne dispose pas de mémoire suffisante, le tueur de MOO est activé, vérifie quel processus occupe actuellement le plus de mémoire, puis tue le processus.

En règle générale, l'erreur Mémoire insuffisante : tuer le processus ou sacrifier un enfant sera déclenchée lorsque la mémoire virtuelle virtuelle disponible (y compris l'espace d'échange) est consommée au point que l'ensemble du système d'exploitation est en danger. Dans ce cas, OOM Killer sélectionne le « processus malveillant » et le tue.

2), exemple de code

3), solution

Bien que l'augmentation de l'espace de swap puisse atténuer l'exception d'espace de tas Java, il est recommandé que la meilleure solution soit de mettre à niveau la mémoire système afin que l'application Java ait suffisamment de mémoire disponible, ce problème ne se produira pas.

En résumé, grâce aux 10 types de situations de débordement de mémoire ci-dessus, tout le monde saura comment résoudre le problème lorsqu'il le rencontrera réellement. Dans le codage réel, vous devez également vous rappeler :

  • Les packages jar tiers doivent être introduits. soigneusement et supprimés résolument Packages jar inutiles pour améliorer la vitesse de compilation et l'utilisation de la mémoire système.

  • Pour les objets volumineux ou les applications à grande mémoire, une optimisation doit être effectuée. Les objets volumineux doivent être traités par tranches pour améliorer les performances de traitement et réduire le cycle de vie des objets.

  • Essayez de fixer le nombre de threads pour vous assurer que la mémoire occupée par les threads est contrôlable. Lorsqu'un grand nombre de threads est nécessaire, le nombre maximum de connexions ouvertes du système d'exploitation doit être optimisé.

  • Pour les appels récursifs, le niveau de récursivité doit également être contrôlé, pas trop élevé et dépasser la profondeur de la stack.

  • Plus la mémoire allouée à la pile n'est pas grande, mieux c'est, car plus la mémoire de la pile est grande et plus il y a de threads, il n'y a pas beaucoup d'espace pour le tas et il est facile de lancer le MOO. Il n'y a généralement aucun problème avec les paramètres par défaut de la JVM (y compris la récursivité).

Tutoriels vidéo associés recommandés : Tutoriel vidéo Java

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!