web-dev-qa-db-fra.com

Traitement de l'erreur "Java.lang.OutOfMemoryError: PermGen space"

Récemment, j'ai rencontré cette erreur dans mon application Web:

Java.lang.OutOfMemoryError: espace PermGen

C'est une application typique Hibernate/JPA + IceFaces/JSF s'exécutant sur Tomcat 6 et JDK 1.6. Apparemment, cela peut se produire après avoir redéployé une application plusieurs fois.

Quelles en sont les causes et que peut-on faire pour l'éviter? Comment puis-je résoudre le problème?

1212
Chris

La solution consistait à ajouter ces indicateurs à la ligne de commande de la machine virtuelle Java au démarrage de Tomcat:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Vous pouvez le faire en arrêtant le service Tomcat, en accédant au répertoire Tomcat/bin et en exécutant Tomcat6w.exe. Sous l'onglet "Java", ajoutez les arguments à la zone "Options Java". Cliquez sur "OK" puis redémarrez le service.

Si vous obtenez une erreur , le service spécifié n'existe pas en tant que service installé , vous devez exécuter:

Tomcat6w //ES//servicename

nom du service est le nom du serveur tel qu'il est affiché dans services.msc.

Source: Le commentaire de orx sur Eric Agile Answers .

560
Chris

Vous feriez mieux d'essayer -XX:MaxPermSize=128M plutôt que _-XX:MaxPermGen=128M_.

Je ne peux pas dire avec précision l'utilisation de ce pool de mémoire, mais cela a à voir avec le nombre de classes chargées dans la JVM. (Par conséquent, l'activation du déchargement de classe pour Tomcat peut résoudre le problème.) Si vos applications génèrent et compilent des classes lors de l'exécution, il est plus probable qu'un pool de mémoire plus grand que celui par défaut soit nécessaire.

250
toesterdahl

Les erreurs les plus courantes sont que l’espace vide et l’espace permanent sont identiques, ce qui n’est pas du tout vrai. Vous pourriez avoir beaucoup d'espace restant dans le tas mais vous pouvez toujours manquer de mémoire en permgen.

Les causes courantes de OutofMemory dans PermGen sont ClassLoader. Chaque fois qu'une classe est chargée dans la machine virtuelle Java, toutes ses métadonnées, ainsi que Classloader, sont conservées dans la zone de PermGen. Elles sont alors récupérées lorsque le chargeur de classe qui les a chargées est prêt pour la récupération de place. Dans Case, Classloader présente une fuite de mémoire. Toutes les classes chargées restent en mémoire et provoquent une perte de mémoire permanente lorsque vous le répétez plusieurs fois. L'exemple classique est Java.lang.OutOfMemoryError: Espace PermGen dans Tomcat .

Maintenant, il y a deux façons de résoudre ce problème:
1. Trouvez la cause de la fuite de mémoire ou s'il y a une fuite de mémoire.
2. Augmentez la taille de l'espace PermGen en utilisant les paramètres JVM -XX:MaxPermSize et -XX:PermSize.

Vous pouvez également vérifier 2 Solution de Java.lang.OutOfMemoryError dans Java pour plus de détails.

67
Peter

Utilisez le paramètre de ligne de commande -XX:MaxPermSize=128m pour une machine virtuelle Sun (en remplaçant évidemment 128 par la taille dont vous avez besoin).

41
user17163

Essayez -XX:MaxPermSize=256m et si cela persiste, essayez -XX:MaxPermSize=512m

37
bassist

Je ajouté-XX: MaxPermSize = 128m (vous pouvez expérimenter ce qui fonctionne le mieux) à Arguments VM car j'utilise Eclipse ide . Dans la plupart des machines virtuelles Java, PermSize par défaut correspond à environ 64 Mo , ce qui manque de mémoire s'il y a trop de classes ou un très grand nombre de classes. Cordes dans le projet.

Pour Eclipse, il est également décrit à la section réponse .

ÉTAPE 1 : double-cliquez sur le serveur Tomcat dans l’onglet Serveurs .

enter image description here

ÉTAPE 2 : Ouvrez le lancement de Conf et ajoutez -XX: MaxPermSize = 128m à la fin des arguments existants de la VM .

enter image description here

27
prayagupd

Je me suis heurté à ce problème tout en déployant et annulant le déploiement d'une application Web complexe, et j'ai pensé ajouter une explication et ma solution.

Lorsque je déploie une application sur Apache Tomcat, un nouveau ClassLoader est créé pour cette application. Le ClassLoader est ensuite utilisé pour charger toutes les classes de l'application et, en cas de non-déploiement, tout est supposé bien partir. Cependant, en réalité, ce n'est pas aussi simple.

Une ou plusieurs des classes créées pendant la vie de l'application Web contiennent une référence statique qui, quelque part sur la ligne, fait référence au ClassLoader. Comme la référence est à l'origine statique, aucune opération de récupération de mémoire ne la nettoyera - le ClassLoader, ainsi que toutes les classes qu'il est chargé, sont là pour rester.

Et après quelques redéploiements, nous rencontrons OutOfMemoryError.

Maintenant, cela est devenu un problème assez grave. Je pouvais m'assurer que Tomcat est redémarré après chaque redéploiement, mais cela détruit tout le serveur, plutôt que l'application en cours de redéploiement, ce qui est souvent impossible.

Alors au lieu de cela, j'ai mis au point une solution en code, qui fonctionne sur Apache Tomcat 6.0. Je n'ai pas testé sur d'autres serveurs d'applications, et je dois souligner que cela risque fort de ne pas fonctionner sans modification sur aucun autre serveur d'applications.

J'aimerais également dire que personnellement je déteste ce code, et que personne ne devrait l'utiliser comme "solution rapide" si le code existant peut être modifié pour utiliser des méthodes d'arrêt et de nettoyage appropriées. Le seul cas où cela devrait être utilisé est s'il existe une bibliothèque externe dont votre code dépend (dans mon cas, il s'agissait d'un client RADIUS) qui ne permet pas de nettoyer ses propres références statiques.

Quoi qu'il en soit, avec le code. Cela devrait être appelé au moment où l'application n'est pas déployée - telle que la méthode destroy d'un servlet ou (la meilleure approche) une méthode contextDestroyed de ServletContextListener.

//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
    try {
        classLoaderClassesField = clazz.getDeclaredField("classes");
    } catch (Exception exception) {
        //do nothing
    }
    clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);

List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));

for (Object o : classes) {
    Class c = (Class)o;
    //Make sure you identify only the packages that are holding references to the classloader.
    //Allowing this code to clear all static references will result in all sorts
    //of horrible things (like Java segfaulting).
    if (c.getName().startsWith("com.whatever")) {
        //Kill any static references within all these classes.
        for (Field f : c.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())
                    && !Modifier.isFinal(f.getModifiers())
                    && !f.getType().isPrimitive()) {
                try {
                    f.setAccessible(true);
                    f.set(null, null);
                } catch (Exception exception) {
                    //Log the exception
                }
            }
        }
    }
}

classes.clear();
22
Edward Torbett

Le message d'espace Java.lang.OutOfMemoryError: PermGen indique que la zone en mémoire de la génération permanente est épuisée.

Toutes les applications Java sont autorisées à utiliser une quantité de mémoire limitée. La quantité exacte de mémoire que votre application particulière peut utiliser est spécifiée lors du démarrage de l'application.

La mémoire Java est séparée en différentes régions, visibles dans l'image suivante:

enter image description here

Métaspace: Un nouvel espace mémoire est né

La JVM HotSpot JDK 8 utilise maintenant la mémoire native pour la représentation des métadonnées de classe et est appelée Metaspace; similaire aux machines Oracle JRockit et IBM.

La bonne nouvelle est que cela signifie qu'il n'y a plus de Java.lang.OutOfMemoryError: PermGen problèmes d'espace et que vous n'avez plus besoin de syntoniser et de surveiller cet espace mémoire à l'aide de Java_8_Download ou supérieur.

17
Santosh Jadi

1) Augmenter la taille de la mémoire de PermGen

La première chose à faire consiste à agrandir la taille de l'espace de la génération permanente. Cela ne peut pas être fait avec les arguments JVM habituels –Xms (définir la taille initiale du segment de mémoire) et –Xmx (définir la taille maximale du segment de mémoire), car, comme cela a été mentionné, l'espace de segmentation de la génération permanente est entièrement séparé du segment de segmentage Java normal. space, et ces arguments définissent l’espace pour cet espace de pile Java normal. Cependant, il existe des arguments similaires qui peuvent être utilisés (au moins avec Sun/OpenJDK jvms) pour agrandir la taille du tas de la génération permanente:

 -XX:MaxPermSize=128m

La valeur par défaut est 64m.

2) Activer le balayage

Une autre façon de s’occuper de cela pour de bon est de permettre aux classes d'être déchargées afin que votre PermGen ne s'épuise jamais:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Des trucs comme ça ont fonctionné magiquement pour moi dans le passé. Une chose cependant, il ya un inconvénient important en termes de performances dans leur utilisation, puisque permgen balayages produiront deux demandes supplémentaires pour chaque demande que vous ferez ou quelque chose du genre. Vous devrez équilibrer votre utilisation avec les compromis.

Vous pouvez trouver les détails de cette erreur.

http://faisalbhagat.blogspot.com/2014/09/Java-outofmemoryerror-permgen.html

15
faisalbhagat

Alternativement, vous pouvez passer à JRockit, qui gère le trafic différemment du jvm de Sun. Il a généralement une meilleure performance aussi.

http://www.Oracle.com/technetwork/middleware/jrockit/overview/index.html

15
Jeremy

La réponse la plus simple de nos jours consiste à utiliser Java 8.

Il ne réserve plus de mémoire exclusivement pour l'espace PermGen, ce qui permet à la mémoire PermGen de se mélanger avec le pool de mémoire normal.

N'oubliez pas que vous devrez supprimer tous les paramètres de démarrage de la JVM -XXPermGen...=... non standard si vous ne voulez pas que Java 8 se plaint de ne rien faire.

13
Edwin Buck

J'ai eu le problème dont nous parlons ici, mon scénario est Eclipse-helios + Tomcat + jsf et ce que vous faisiez est de déployer une application simple sur Tomcat. Je montrais le même problème ici, résolu comme suit.

Dans Eclipse, allez sur serveurs onglet double-cliquez sur le serveur enregistré dans mon cas Tomcat 7.0, il ouvre les informations générales d'enregistrement de mon serveur de fichiers. Sur la section "Informations générales" cliquez sur le lien "Ouvrir la configuration de lancement", cela ouvre l'exécution des options du serveur dans l'onglet Arguments de VM arguments ajoutés à la fin de ces deux entrées

-XX: MaxPermSize = 512m
-XX: PermSize = 512m

et prêt.

13
Hugo Mendoza
  1. Ouvrez Tomcat7w à partir du répertoire bin de Tomcat ou tapez Monitor Tomcat dans le menu de démarrage (une fenêtre à onglets s'ouvre avec diverses informations de service).
  2. Dans la zone de texte Java Options, ajoutez cette ligne:

    -XX:MaxPermSize=128m
    
  3. Définissez le pool de mémoire initiale sur 1024 (facultatif).
  4. Définissez le pool de mémoire maximum sur 1024 (facultatif).
  5. Cliquez sur OK.
  6. Redémarrez le service Tomcat.
8
Lucky

Une erreur d'espace permanent est générée en raison de l'utilisation d'un grand espace plutôt que de l'espace fourni par jvm pour exécuter le code. La meilleure solution à ce problème dans les systèmes d'exploitation UNIX consiste à modifier une configuration sur un fichier bash. Les étapes suivantes résolvent le problème.

Exécutez la commande gedit .bashrc sur le terminal.

Créez la variable Java_OTPS avec la valeur suivante:

export Java_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"

Enregistrez le fichier bash. Exécutez la commande exec bash sur le terminal. Redémarrez le serveur.

J'espère que cette approche fonctionnera sur votre problème. Si vous utilisez une version Java inférieure à 8, ce problème se produit parfois. Mais si vous utilisez Java 8, le problème ne se produit jamais.

6
Darshan

Augmenter la taille de la génération permanente ou peaufiner les paramètres du GC ne vous aidera PAS si vous avez une fuite de mémoire réelle. Si votre application ou une bibliothèque tierce qu'elle utilise utilise des fuites, la seule solution réelle et permanente consiste à rechercher cette fuite et à la réparer. De nombreux outils peuvent vous aider. L’un des plus récents est Plumbr , qui vient de publier une nouvelle version avec les fonctionnalités requises.

5
Nikem

De plus, si vous utilisez log4j dans votre application Web, vérifiez ce paragraphe dans log4j documentation .

Il semble que si vous utilisez PropertyConfigurator.configureAndWatch("log4j.properties"), vous causez des fuites de mémoire lorsque vous annulez le déploiement de votre application Web.

5

jrockit résolu cela pour moi aussi; Cependant, j'ai remarqué que les temps de redémarrage des servlets étaient bien pires. Par conséquent, même s'il était meilleur en production, cela ralentissait le développement.

4
Tim Howland

J'ai une combinaison de Hibernate + Eclipse RCP, essayé d'utiliser -XX:MaxPermSize=512m et -XX:PermSize=512m et il semble fonctionner pour moi.

4
Pankaj Shinde

J'ai essayé plusieurs réponses et la seule chose qui a finalement fait le travail était cette configuration pour le plugin compilateur dans le pom:

<plugin>
    <groupId>org.Apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <fork>true</fork>
        <meminitial>128m</meminitial>
        <maxmem>512m</maxmem>
        <source>1.6</source>
        <target>1.6</target>
        <!-- prevent PermGen space out of memory exception -->
        <!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
    </configuration>
</plugin>

espérons que celui-ci aide.

4
kiwilisk

Définissez -XX:PermSize=64m -XX:MaxPermSize=128m. Plus tard, vous pouvez aussi essayer d'augmenter MaxPermSize. J'espère que ça va marcher. La même chose fonctionne pour moi. Régler uniquement MaxPermSize n'a pas fonctionné pour moi.

4
sandeep

L'affectation de plus de mémoire à Tomcat n'est PAS la solution appropriée.

La bonne solution consiste à effectuer un nettoyage une fois le contexte détruit et recréé (déploiement à chaud). La solution consiste à arrêter les fuites de mémoire.

Si votre serveur Tomcat/Webapp vous dit que l'échec de la désinscription des pilotes (JDBC) a échoué, annulez-les. Cela arrêtera les fuites de mémoire.

Vous pouvez créer un ServletContextListener et le configurer dans votre web.xml. Voici un exemple ServletContextListener:

import Java.sql.Driver;
import Java.sql.DriverManager;
import Java.sql.SQLException;
import Java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.Apache.log4j.Logger;

import com.mysql.jdbc.AbandonedConnectionCleanupThread;

/**
 * 
 * @author alejandro.tkachuk / calculistik.com
 *
 */
public class AppContextListener implements ServletContextListener {

    private static final Logger logger = Logger.getLogger(AppContextListener.class);

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        logger.info("AppContextListener started");
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        logger.info("AppContextListener destroyed");

        // manually unregister the JDBC drivers
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("Unregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error unregistering driver %s", driver), e);
            }

        }

        // manually shutdown clean up threads
        try {
            AbandonedConnectionCleanupThread.shutdown();
            logger.info("Shutting down AbandonedConnectionCleanupThread");
        } catch (InterruptedException e) {
            logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
            e.printStackTrace();
        }        
    }
}

Et ici, vous le configurez dans votre web.xml:

<listener>
    <listener-class>
        com.calculistik.mediweb.context.AppContextListener 
    </listener-class>
</listener>  

Ils disent que la dernière version de Tomcat (6.0.28 ou 6.0.29) gère la tâche de redéploiement des servlets beaucoup mieux.

3
Tony Ennis

La première étape dans ce cas consiste à vérifier si le CPG est autorisé à décharger des classes de PermGen. La JVM standard est plutôt conservatrice à cet égard - les classes sont nées pour vivre éternellement. Ainsi, une fois chargées, les classes restent en mémoire même si aucun code ne les utilise plus. Cela peut devenir un problème lorsque l'application crée de nombreuses classes de manière dynamique et que les classes générées ne sont pas nécessaires pendant des périodes plus longues. Dans un tel cas, il peut être utile de permettre à la JVM de décharger les définitions de classe. Cela peut être réalisé en ajoutant un seul paramètre de configuration à vos scripts de démarrage:

-XX:+CMSClassUnloadingEnabled

Par défaut, la valeur est false. Pour l'activer, vous devez définir explicitement l'option suivante dans les options Java. Si vous activez CMSClassUnloadingEnabled, GC balayera également PermGen et supprimera les classes qui ne sont plus utilisées. Gardez à l'esprit que cette option ne fonctionnera que si UseConcMarkSweepGC est également activé à l'aide de l'option ci-dessous. Ainsi, lorsque vous exécutez ParallelGC ou, Dieu nous en préserve, Serial GC, assurez-vous que vous avez défini votre GC sur CMS en spécifiant:

-XX:+UseConcMarkSweepGC
3
sendon1982

Je rencontre exactement le même problème, mais malheureusement aucune des solutions suggérées ne fonctionnait vraiment pour moi. Le problème ne s'est pas produit pendant le déploiement et je ne faisais aucun déploiement à chaud.

Dans mon cas, le problème se produisait à chaque fois au même moment de l'exécution de mon application Web, lors de la connexion (via hibernation) à la base de données.

Ce lien (également mentionné plus haut) a fourni suffisamment d'éléments internes pour résoudre le problème. Déplacer le pilote jdbc (mysql) du WEB-INF vers le dossier jre/lib/ext/semble avoir résolu le problème. Ce n'est pas la solution idéale, car la mise à niveau vers un JRE plus récent nécessiterait la réinstallation du pilote. Log4j est un autre candidat qui pourrait causer des problèmes similaires, vous pouvez donc également le déplacer.

3
Maze

La configuration de la mémoire dépend de la nature de votre application.

Que faites-vous?

Quel est le montant des transactions nécessaires?

Combien de données chargez-vous?

etc.

etc.

etc

Vous pourriez probablement profiler votre application et commencer à nettoyer certains modules de votre application.

Apparemment, cela peut se produire après avoir redéployé une application plusieurs fois

Tomcat a un déploiement à chaud mais consomme de la mémoire. Essayez de redémarrer votre conteneur de temps en temps. De plus, vous aurez besoin de connaître la quantité de mémoire nécessaire pour fonctionner en mode production, cela semble être un bon moment pour cette recherche.

3
OscarRyz

Si vous obtenez cela dans l'EDI Eclipse, même après avoir défini les paramètres --launcher.XXMaxPermSize, -XX:MaxPermSize, etc., toujours si vous obtenez la même erreur, il est fort probable que Eclipse utilise un buggy. version de JRE qui aurait été installée par certaines applications tierces et définie par défaut. Ces versions boguées ne récupèrent pas les paramètres PermSize et donc, peu importe ce que vous définissez, vous continuez à avoir ces erreurs de mémoire. Alors, dans votre Eclipse.ini, ajoutez les paramètres suivants:

-vm <path to the right JRE directory>/<name of javaw executable>

Assurez-vous également de définir le JRE par défaut dans les préférences d’Eclipse sur la version correcte de Java.

2
Hrishikesh Kumar

La seule façon qui a fonctionné pour moi était avec la JVM JRockit. J'ai MyEclipse 8.6.

Le segment de mémoire de la machine virtuelle Java stocke tous les objets générés par un programme en cours d'exécution Java. Java utilise l'opérateur new pour créer des objets et la mémoire pour les nouveaux objets est allouée sur le tas au moment de l'exécution. Le garbage collection est le mécanisme permettant de libérer automatiquement la mémoire contenue par les objets qui ne sont plus référencés par le programme.

2
Daniel

J'avais un problème similaire. Le mien est JDK 7 + Maven 3.0.2 + Struts 2.0 + Projet basé sur l'injection de dépendance Google GUICE.

Chaque fois que j'ai essayé d'exécuter mvn clean package la commande, elle affichait l'erreur suivante et "BUILD FAILURE" s'est produite

org.Apache.maven.surefire.util.SurefireReflectionException: Java.lang.reflect.InvocationTargetException; l'exception imbriquée est Java.lang.reflect.InvocationTargetException: null Java.lang.reflect.InvocationTargetException Causée par: Java.l. OutOfMemoryError: espace PermGen

J'ai essayé tous les trucs et astuces utiles ci-dessus, mais malheureusement, aucun n'a fonctionné pour moi. Ce qui a fonctionné pour moi est décrit étape par étape ci-dessous: =>

  1. Allez à votre pom.xml
  2. Rechercher _<artifactId>maven-surefire-plugin</artifactId>_
  3. Ajoutez un nouvel élément _<configuration>_ puis un sous-élément _<argLine>_ dans lequel passez _-Xmx512m -XX:MaxPermSize=256m_ comme indiqué ci-dessous =>

<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

J'espère que ça aide, bonne programmation :)

2
NIKHIL CHAURASIA

"Ils" ont tort car j'utilise la version 6.0.29 et j'ai le même problème, même après avoir défini toutes les options. Comme Tim Howland l’a dit plus haut, ces options ne font que retarder l’inévitable. Ils me permettent de redéployer 3 fois avant de générer l'erreur au lieu de chaque redéploiement.

2
Ross Peoples

Vous pouvez aussi résoudre ce problème en faisant un:

rm -rf <Tomcat-dir>/work/* <Tomcat-dir>/temp/*

En effaçant les répertoires travail et temp, Tomcat effectue un démarrage en mode minimal.

1
dev

Si quelqu'un est aux prises avec la même erreur dans netbeans, voici comment je l'ai corrigé.

Dans Netbeans:

Allez dans l'onglet Services -> Droit sur le serveur -> Choisissez les propriétés -> Allez dans l'onglet Plate-forme -> Dans les options vm, tapez -Xms1024m.

Dans mon cas, j'ai donné -Xms4096m

Voici la capture d'écran:

enter image description here

0
Satyadev