Comment gérer l'erreur «java.lang.OutOfMemoryError: Java heap space»?

416

J'écris une application Swing côté client (concepteur de polices graphiques) sur Java 5 . Récemment, je rencontre une java.lang.OutOfMemoryError: Java heap spaceerreur car je ne suis pas conservateur sur l'utilisation de la mémoire. L'utilisateur peut ouvrir un nombre illimité de fichiers et le programme conserve les objets ouverts en mémoire. Après une recherche rapide, j'ai trouvé l' ergonomie dans la machine virtuelle Java 5.0 et d'autres disant sur la machine Windows que la taille maximale du tas par défaut de la JVM était 64MB.

Face à cette situation, comment gérer cette contrainte?

Je pourrais augmenter la taille maximale du tas en utilisant l' option de ligne de commande pour Java, mais cela nécessiterait de déterminer la RAM disponible et d'écrire un programme ou un script de lancement. De plus, augmenter jusqu'à un maximum fini ne résout pas finalement le problème.

Je pourrais réécrire une partie de mon code pour conserver fréquemment des objets dans le système de fichiers (utiliser la base de données est la même chose) pour libérer de la mémoire. Cela pourrait fonctionner, mais c'est probablement beaucoup de travail aussi.

Si vous pouviez me signaler les détails des idées ci-dessus ou certaines alternatives comme la mémoire virtuelle automatique, l'extension dynamique de la taille du segment de mémoire , ce serait parfait.

Eugene Yokota
la source
La taille de segment de mémoire maximale par défaut de 64 Mo est antérieure à J2SE 5.0. Pour des informations sur J2SE 8.0, voir "Ergonomie du récupérateur de déchets" sur docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas
Si vous avez atterri ici parce que chaque question de MOO est doublée à celle-ci, assurez-vous également de vérifier: stackoverflow.com/questions/299659/… Il fournit la solution pour nettoyer les références de mémoire `` juste à temps '' avant le MOO. SoftReferences peut être l'outil qui résout votre problème réel.
Steve Steiner

Réponses:

244

En fin de compte, vous avez toujours un maximum fini de tas à utiliser, quelle que soit la plateforme sur laquelle vous exécutez. Dans Windows 32 bits, c'est autour 2GB(pas spécifiquement le tas mais la quantité totale de mémoire par processus). Il se trouve que Java choisit de réduire la valeur par défaut (probablement pour que le programmeur ne puisse pas créer de programmes qui ont une allocation de mémoire incontrôlable sans se heurter à ce problème et sans avoir à examiner exactement ce qu'ils font).

Donc, cela étant donné, vous pouvez adopter plusieurs approches pour déterminer la quantité de mémoire dont vous avez besoin ou pour réduire la quantité de mémoire que vous utilisez. Une erreur courante avec les langages récupérés comme Java ou C # est de conserver les références aux objets que vous n'utilisez plus ou d'allouer de nombreux objets lorsque vous pouvez les réutiliser à la place. Tant que les objets ont une référence à eux, ils continueront à utiliser l'espace de tas car le garbage collector ne les supprimera pas.

Dans ce cas, vous pouvez utiliser un profileur de mémoire Java pour déterminer quelles méthodes de votre programme allouent un grand nombre d'objets, puis déterminer s'il existe un moyen de vous assurer qu'ils ne sont plus référencés ou de ne pas les allouer en premier lieu. Une option que j'ai utilisée dans le passé est "JMP" http://www.khelekore.org/jmp/ .

Si vous déterminez que vous allouez ces objets pour une raison et que vous devez conserver les références (selon ce que vous faites, cela peut être le cas), vous n'aurez qu'à augmenter la taille maximale du tas lorsque vous démarrez le programme. Cependant, une fois que vous effectuez le profilage de la mémoire et comprenez comment vos objets sont alloués, vous devriez avoir une meilleure idée de la quantité de mémoire dont vous avez besoin.

En général, si vous ne pouvez pas garantir que votre programme fonctionnera dans une certaine quantité de mémoire (peut-être en fonction de la taille d'entrée), vous rencontrerez toujours ce problème. Ce n'est qu'après avoir épuisé tout cela que vous devrez étudier la mise en cache des objets sur le disque, etc. vos algorithmes ou modèles d'allocation de mémoire. En général, cela ne sera généralement le cas que pour les algorithmes fonctionnant sur de grands ensembles de données (comme une base de données ou un programme d'analyse scientifique), puis des techniques telles que la mise en cache et les entrées-sorties mappées en mémoire deviennent utiles.

Ben Childs
la source
6
OpenJDK et OracleJDK ont groupé le profileur - jvisualvm. Si vous voulez plus de commodités, je vous suggère votre kit commercial Yourkit.
Petr Gladkikh
121

Exécutez Java avec l'option de ligne de commande -Xmx, qui définit la taille maximale du tas.

Voir ici pour plus de détails .

Dave Webb
la source
3
Comment définir ce paramètre pour toujours? Parce que j'utilise la commande 'gradlew assemble'.
Dr.jacky
2
Exécuter-> Exécuter les configurations-> Cliquez sur les arguments-> à l'intérieur du type d'arguments VM -Xms1g -Xmx2g
Arayan Singh
2
Voilà la vraie réponse.
nccc
85

Vous pouvez spécifier par projet combien d'espace de stockage votre projet veut

Ce qui suit est pour Eclipse Helios / Juno / Kepler :

Clic droit de la souris sur

 Run As - Run Configuration - Arguments - Vm Arguments, 

puis ajoutez ceci

-Xmx2048m
allenhwkim
la source
1
salut bighostkim et cuongHuyTo, où est "Arguments" .. je peux voir jusqu'à Run Run Configuration. Veuillez me téléphoner. Mon besoin de télécharger et de stocker près de 2000 contacts depuis gmail. Il se bloque en raison d'une exception de mémoire
insuffisante
@AndroiRaji: vous cliquez avec le bouton droit de la souris sur la classe Java qui a un exécutable principal (c'est-à-dire "public static void main (String [] args)"), puis choisissez Exécuter en tant que - Exécuter la configuration. Ensuite, "Arguments" est l'onglet juste après le Main (vous voyez les onglets Main, Arguments, JRE, Classpath, Source, Environment, Common).
CuongHuyTo
47

Augmenter la taille du tas n'est pas un "correctif", c'est un "plâtre", 100% temporaire. Il se plantera à nouveau ailleurs. Pour éviter ces problèmes, écrivez du code haute performance.

  1. Utilisez autant que possible des variables locales.
  2. Assurez-vous de sélectionner l'objet correct (EX: sélection entre String, StringBuffer et StringBuilder)
  3. Utilisez un bon système de code pour votre programme (EX: Utilisation de variables statiques VS de variables non statiques)
  4. D'autres trucs qui pourraient fonctionner sur votre code.
  5. Essayez de bouger avec multy THREADING
Jus de citron
la source
C'est tellement vrai. J'essaie de résoudre un problème où j'obtiens OOM sur un thread AWT mais si j'utilise un nouveau thread différent, je n'obtiens pas de problème OOM. Tout ce que je peux trouver en ligne est d'augmenter la taille du tas pour le thread AWT.
Ashish
@Ash: Oui, corrigez le problème principal au lieu de chercher des enduits.
Lemon Juice
Le garbage collection et l'approche de gestion de la mémoire en Java étaient censés résoudre toutes ces complications malloc-dealloc de ses prédécesseurs :( Bien sûr, je suis entièrement d'accord avec cette réponse, c'est juste dommage que les valeurs par défaut ne facilitent pas l'écriture de code avec des données lean - structures nettoyées le plus tôt possible
Davos
31

Grande mise en garde ---- dans mon bureau, nous constations que (sur certaines machines Windows) nous ne pouvions pas allouer plus de 512 m pour le tas Java. Cela s'est avéré être dû au produit antivirus Kaspersky installé sur certaines de ces machines. Après avoir désinstallé ce produit AV, nous avons constaté que nous pouvions allouer au moins 1,6 Go, c'est-à-dire que -Xmx1600m(m est obligatoire , sinon cela conduira à une autre erreur "Trop petit tas initial").

Aucune idée si cela se produit avec d'autres produits AV, mais cela se produit probablement parce que le programme AV réserve un petit bloc de mémoire dans chaque espace d'adressage, empêchant ainsi une seule allocation vraiment importante.

David
la source
22

Les arguments VM ont fonctionné pour moi dans Eclipse. Si vous utilisez la version 3.4 d'éclipse, procédez comme suit

allez dans Run --> Run Configurations -->puis sélectionnez le projet sous maven build -> puis sélectionnez l'onglet "JRE" -> puis entrez -Xmx1024m.

Sinon, vous pouvez faire Run --> Run Configurations --> select the "JRE" tab -->ensuite entrer -Xmx1024m

Cela devrait augmenter le tas de mémoire pour tous les builds / projets. La taille de mémoire ci-dessus est de 1 Go. Vous pouvez optimiser la façon dont vous le souhaitez.

aimer tout
la source
18

Oui, -Xmxvous pouvez configurer plus de mémoire pour votre JVM. Pour être sûr de ne pas fuir ni perdre de mémoire. Faites un vidage de tas et utilisez l'analyseur de mémoire Eclipse pour analyser votre consommation de mémoire.

kohlerm
la source
JVMJ9VM007E Option de ligne de commande non reconnue: -Xmx Impossible de créer la machine virtuelle Java. Downvote
Philip Rego
17

Je voudrais ajouter des recommandations de dépannage Oracle article de .

Exception dans le thread thread_name: java.lang.OutOfMemoryError: Java heap space

L'espace de tas Java du message de détail indique que l'objet n'a pas pu être alloué dans le tas Java. Cette erreur n'implique pas nécessairement une fuite de mémoire

Causes possibles:

  1. Problème de configuration simple , où la taille de segment de mémoire spécifiée est insuffisante pour l'application.

  2. L'application contient involontairement des références à des objets , ce qui empêche les objets d'être récupérés.

  3. Utilisation excessive de finaliseurs .

Une autre source potentielle de cette erreur survient avec les applications qui font un usage excessif des finaliseurs. Si une classe a une méthode de finalisation, les objets de ce type n'ont pas leur espace récupéré au moment de la récupération de place

Après le garbage collection , les objets sont mis en file d'attente pour la finalisation , qui se produit ultérieurement. les finaliseurs sont exécutés par un thread démon qui dessert la file d'attente de finalisation. Si le thread du finaliseur ne peut pas suivre la file d'attente de finalisation, le tas Java pourrait se remplir et ce type d' exception OutOfMemoryError serait levé .

Un scénario qui peut provoquer cette situation est lorsqu'une application crée des threads de haute priorité qui provoquent l' augmentation de la file d'attente de finalisation à une vitesse plus rapide que la vitesse à laquelle le thread du finaliseur traite cette file d'attente.

Ravindra babu
la source
9

Suivez les étapes ci-dessous:

  1. Ouvert catalina.sh depuis tomcat / bin.

  2. Remplacez JAVA_OPTS par

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Redémarrez votre tomcat

Pradip Bhatt
la source
8

J'ai lu ailleurs que vous pouvez essayer - attrapez java.lang.OutOfMemoryError et sur le bloc catch, vous pouvez libérer toutes les ressources que vous savez utiliser beaucoup de mémoire, fermer les connexions et ainsi de suite, puis faire une System.gc()nouvelle tentative tu allais faire.

Une autre façon est la suivante, même si je ne sais pas si cela fonctionnerait, mais je teste actuellement si cela fonctionnera sur mon application.

L'idée est de faire la récupération de place en appelant System.gc () qui est connu pour augmenter la mémoire libre. Vous pouvez continuer à vérifier cela après l'exécution d'un code avalant de la mémoire.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
mwangi
la source
6
En général, je pense que la JVM préférera le Garbage Collect (GC) plutôt que de lancer une OutOfMemoryError. L'appel explicite de System.gc () après OutOfMemoryError pourrait éventuellement aider sur certaines machines virtuelles / configurations, mais je ne m'attendrais pas à ce que cela fonctionne très bien dans le cas général. Cependant, supprimer des références d'objets inutiles aiderait certainement dans presque tous les cas.
Mike Clark
6
@mwangi Appeler System.gc () directement à partir du code est généralement une mauvaise idée. C'est juste une suggestion à JVM que GC doit être exécuté, mais il n'y a absolument aucune garantie qu'il sera exécuté.
7

Un moyen simple de résoudre OutOfMemoryErroren java consiste à augmenter la taille maximale du segment de mémoire en utilisant les options JVM-Xmx512M , cela résoudra immédiatement votre OutOfMemoryError. C'est ma solution préférée lorsque j'obtiens OutOfMemoryError dans Eclipse, Maven ou ANT lors de la construction du projet, car en fonction de la taille du projet, vous pouvez facilement manquer de mémoire.

Voici un exemple d'augmentation de la taille maximale du segment de mémoire de la machine virtuelle Java. Il est également préférable de conserver le rapport -Xmx à -Xms soit 1: 1 ou 1: 1,5 si vous définissez la taille de segment dans votre application java.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Lien de référence

chaukssey
la source
1
Une idée pourquoi nous devons les garder dans un rapport 1: 1 ou 1: 1,5?
ernesto
7

Par défaut pour le développement, la JVM utilise une petite taille et une petite configuration pour d'autres fonctionnalités liées aux performances. Mais pour la production, vous pouvez régler par exemple (en plus, il peut exister une configuration spécifique à Application Server) -> (S'il n'y a toujours pas assez de mémoire pour satisfaire la demande et que le tas a déjà atteint la taille maximale, une OutOfMemoryError se produira)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Par exemple: Sur la plate-forme linux pour le mode de production, les paramètres préférables.

Après avoir téléchargé et configuré le serveur de cette façon http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.créer le fichier setenv.sh dans le dossier / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2.Ouvrez et écrivez ces paramètres pour définir le mode préférable.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

Notez que la JVM utilise plus de mémoire que le tas. Par exemple, les méthodes Java, les piles de threads et les descripteurs natifs sont alloués dans une mémoire distincte du tas, ainsi que des structures de données internes JVM.

Musa
la source
7

J'ai rencontré le même problème de taille de tas java.

J'ai deux solutions si vous utilisez Java 5 (1.5).

  1. installez simplement jdk1.6 et allez dans les préférences d'éclipse et définissez le chemin jre de jav1 1.6 comme vous l'avez installé.

  2. Vérifiez votre argument VM et laissez-le être quel qu'il soit. ajoutez simplement une ligne ci-dessous de tous les arguments présents dans les arguments de la machine virtuelle comme -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Je pense que cela va fonctionner...

Soumya Sandeep Mohanty
la source
7

Si vous avez besoin de surveiller votre utilisation de la mémoire au moment de l'exécution, le java.lang.managementpackage propose MBeansqui peut être utilisé pour surveiller les pools de mémoire dans votre machine virtuelle (par exemple, l'espace eden, la génération sécurisée, etc.), ainsi que le comportement de récupération de place.

L'espace de mémoire libre signalé par ces MBeans variera considérablement en fonction du comportement du GC, en particulier si votre application génère un grand nombre d'objets qui seront ultérieurement édités par le GC. Une approche possible consiste à surveiller l'espace de tas libre après chaque GC complet, que vous pourrez peut-être utiliser pour prendre une décision sur la libération de mémoire par des objets persistants.

En fin de compte, votre meilleur pari est de limiter autant que possible votre rétention de mémoire tandis que les performances restent acceptables. Comme indiqué précédemment, la mémoire est toujours limitée, mais votre application doit avoir une stratégie pour gérer l'épuisement de la mémoire.

Leigh
la source
5

Notez que si vous en avez besoin dans une situation de déploiement, envisagez d'utiliser Java WebStart (avec une version "ondisk", pas la version réseau - possible dans Java 6u10 et versions ultérieures) car cela vous permet de spécifier les différents arguments de la JVM en croix façon plate-forme.

Sinon, vous aurez besoin d'un lanceur spécifique au système d'exploitation qui définit les arguments dont vous avez besoin.

Thorbjørn Ravn Andersen
la source
Java WebStart est en cours de suppression. Je ne connais pas encore de remplacement approprié.
Thorbjørn Ravn Andersen
1

Si ce problème se produit dans Wildfly 8 et JDK1.8, nous devons alors spécifier les paramètres MaxMetaSpace au lieu des paramètres PermGen.

Par exemple, nous devons ajouter la configuration ci-dessous dans le fichier setenv.sh de wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Pour plus d'informations, veuillez consulter Wildfly Heap Issue

satish
la source
1

En ce qui concerne les netbeans, vous pouvez définir la taille maximale du tas pour résoudre le problème.

Allez dans 'Exécuter', puis -> 'Définir la configuration du projet' -> 'Personnaliser' -> 'exécuter' de sa fenêtre contextuelle -> 'Option VM' -> remplissez '-Xms2048m -Xmx2048m' .

Xiaogang
la source
1

Si vous continuez à allouer et à conserver des références à un objet, vous remplirez toute la quantité de mémoire dont vous disposez.

Une option consiste à fermer et ouvrir un fichier transparent lorsqu'il change d'onglet (vous ne gardez qu'un pointeur sur le fichier, et lorsque l'utilisateur change d'onglet, vous fermez et nettoyez tous les objets ... cela rendra le changement de fichier plus lent ... mais ...), et peut-être ne garder que 3 ou 4 fichiers en mémoire.

Une autre chose que vous devez faire est, lorsque l'utilisateur ouvre un fichier, le charger et intercepter toute OutOfMemoryError, puis (comme il n'est pas possible d'ouvrir le fichier) fermer ce fichier, nettoyer ses objets et avertir l'utilisateur qu'il doit fermer inutilisé des dossiers.

Votre idée d'étendre dynamiquement la mémoire virtuelle ne résout pas le problème, car la machine est limitée sur les ressources, vous devez donc être prudent et gérer les problèmes de mémoire (ou du moins, soyez prudent avec eux).

Voici quelques conseils que j'ai vus avec les fuites de mémoire:

-> Gardez à l'esprit que si vous mettez quelque chose dans une collection et que vous l'oubliez ensuite, vous y avez toujours une référence forte, alors annulez la collection, nettoyez-la ou faites-en quelque chose ... sinon, vous trouverez un fuite de mémoire difficile à trouver.

-> Peut-être, l'utilisation de collections avec des références faibles (faiblessehashmap ...) peut aider avec des problèmes de mémoire, mais vous devez être prudent avec cela, car vous pourriez constater que l'objet que vous recherchez a été collecté.

-> Une autre idée que j'ai trouvée est de développer une collection persistante stockée sur les objets de base de données les moins utilisés et chargés de manière transparente. Ce serait probablement la meilleure approche ...

SoulWanderer
la source
0

Si tout le reste échoue, en plus d'augmenter la taille maximale du tas, essayez également d'augmenter la taille du swap. Pour Linux, à partir de maintenant, les instructions pertinentes peuvent être trouvées dans https://linuxize.com/post/create-a-linux-swap-file/ .

Cela peut être utile si vous compilez par exemple quelque chose de grand dans une plate-forme embarquée.

nccc
la source