Question

Récemment, cette erreur s'est produite dans mon application Web:

  

java.lang.OutOfMemoryError: espace PermGen

C’est une application typique Hibernate / JPA + IceFaces / JSF fonctionnant sur Tomcat 6 et JDK 1.6. Cela peut apparemment 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 résoudre le problème?

Était-ce utile?

La solution

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Vous pouvez le faire en arrêtant le service tomcat, en allant dans le répertoire Tomcat / bin et en exécutant tomcat6w.exe. Sous la rubrique "Java". onglet, ajoutez les arguments à la section "Options Java". boîte. 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_service est le nom du serveur tel qu'il est affiché dans services.msc

Source: le commentaire d’orx sur La réponse agile d'Eric .

Autres conseils

Vous devriez essayer -XX: MaxPermSize = 128 M plutôt que -XX: MaxPermGen = 128 M .

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.

Les erreurs PermGen sur le serveur d'applications qui se produisent après plusieurs déploiements sont probablement dues à des références détenues par le conteneur dans les chargeurs de classe de vos anciennes applications. Par exemple, l'utilisation d'une classe de niveau de journalisation personnalisée fera que les références seront conservées par le chargeur de classes du serveur d'applications. Vous pouvez détecter ces fuites entre les chargeurs de classe en utilisant des outils d'analyse JVM modernes (JDK6 +) tels que jmap et jhat pour déterminer quelles classes continuent d'être conservées dans votre application, et de modifier ou d'éliminer leur utilisation. Les suspects habituels sont les bases de données, les enregistreurs et autres bibliothèques de niveau base-cadre.

Voir Une fuite de chargeur de classe: le java redouté. lang.OutOfMemoryError: espace PermGen " exception , et en particulier son post de suivi .

Les gens commettent souvent les erreurs suivantes: l’espace de tas et l’espace de permgen 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 dont toutes les classes chargées resteront en mémoire, ce qui provoquera une perte de mémoire permanente lorsque vous le répétez plusieurs fois. L'exemple classique est Java.lang.OutOfMemoryError: PermGen Space dans Tomcat .

Maintenant, il existe deux façons de résoudre ce problème:
1. Recherchez la cause d'une fuite de mémoire ou s'il y a une fuite de mémoire.
2. Augmentez la taille de l'espace PermGen en utilisant le paramètre -XX: MaxPermSize et le -XX: PermSize de la machine virtuelle Java.

Vous pouvez également consulter 2 Solution de Java.lang.OutOfMemoryError en Java pour plus de détails.

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

Essayez -XX: MaxPermSize = 256m et s'il persiste, essayez -XX: MaxPermSize = 512m

I ajouté -XX: MaxPermSize = 128m (vous pouvez expérimenter ce qui fonctionne le mieux) pour Arguments de la machine virtuelle lorsque j'utilise eclipse ide. Dans la plupart des machines virtuelles Java, PermSize par défaut est d'environ 64 Mo qui manque de mémoire s'il y a trop de classes ou de chaînes dans le projet.

Pour eclipse, il est également décrit à la answer .

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

.

entrer la description de l'image ici

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

entrer la description de l'image ici

Je me suis heurté à ce problème lors du déploiement et de l'annulation du déploiement d'une application Web complexe, et j'ai pensé ajouter une explication et une 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 ramassage des ordures ne la nettoyera - le ClassLoader et toutes les classes qu'il charge sont là pour rester.

Et après quelques redéploiements, nous rencontrons l’erreur 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.

Au lieu de cela, j’ai mis en place une solution en code, qui fonctionne sur Apache Tomcat 6.0. Je n'ai testé sur aucun autre serveur d'applications et je dois souligner que cela ne fonctionnera probablement pas sans modification sur aucun autre serveur d'application .

J'aimerais également dire que personnellement, je déteste ce code et que personne ne devrait l'utiliser comme "solution miracle". si le code existant peut être modifié pour utiliser les méthodes d’arrêt et de nettoyage appropriées . Cela ne doit être utilisé que 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, comme la méthode de destruction d’un servlet ou (meilleure approche) la 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();

Vous pouvez également passer à JRockit, qui gère le trafic différemment du jvm de sun. En général, ses performances sont également meilleures.

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

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

La première chose à faire est de agrandir la taille de l’espace mémoire de la génération permanente. Cela ne peut pas être fait avec les arguments JVM habituels et les arguments JVM habituels et la taille & # 8211; Xmx (taille maximale du segment mémoire), car, comme indiqué, l'espace de segmentation de génération permanente est entièrement séparé du segment de mémoire Java standard. espace, et ces arguments définissent l’espace pour cet espace de mémoire normal Java. Cependant, il existe des arguments similaires qui peuvent être utilisés (au moins avec Sun / OpenJDK jvms) pour agrandir la taille de la génération permanente:

 -XX:MaxPermSize=128m

La valeur par défaut est 64 m.

2) Activer le balayage

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
Des choses comme celle-là ont fonctionné magiquement pour moi dans le passé. Une chose cependant, il ya un inconvénient important en termes de performances de leur utilisation, car les analyses permgen génèreront environ 2 demandes supplémentaires pour chaque demande que vous ferez ou quelque chose du genre. Vous devez é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

Le message d'espace java.lang.OutOfMemoryError: PermGen indique que la zone 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 au démarrage de l'application.

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

 entrer la description de l'image ici

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

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

La bonne nouvelle est que cela signifie qu'il n'y a plus de java.lang.OutOfMemoryError: PermGen et qu'il n'est plus nécessaire d'ajuster et de surveiller cet espace mémoire à l'aide de Java_8_Download ou supérieur.

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

Dans Eclipse, accédez à l'onglet Serveurs , double-cliquez sur le serveur enregistré dans mon cas, tomcat 7.0, il ouvre les informations d'enregistrement générales de mon serveur de fichiers. Dans la section "Informations générales" , cliquez sur le lien "Ouvrir la configuration de lancement" . Ceci ouvre l'exécution des options du serveur dans l'onglet Arguments des arguments de la MV ajoutés dans la liste. mettre fin à ces deux entrées

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

et prêt.

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 JVM -XXPermGen ... = ... non standard si vous ne voulez pas que Java 8 se plaint de ne pas le faire. faire n'importe quoi.

  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 Options Java, 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.

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 une 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 avec 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 reproduira jamais.

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. Plumbr est l'un des outils les plus récents, qui vient de publier une nouvelle version avec la version requise. capacités.

Aussi, 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") , des fuites de mémoire surviennent lorsque vous annulez le déploiement de votre application Web.

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

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

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.

a résolu ce problème pour moi également; Cependant, j’ai remarqué que les temps de redémarrage des servlets étaient bien pires. Ainsi, même s’il était meilleur en production, c’était un peu un frein dans le développement.

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

Qu'est-ce que tu fais?

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 plusieurs redéploiements d'une application

Tomcat est déployé à chaud, mais il 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.

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.

Je rencontre exactement le même problème, mais malheureusement aucune des solutions suggérées n’a vraiment fonctionné 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 est survenu à chaque fois au même moment de l'exécution de mon application Web lors de la connexion (via hibernate) à la base de données.

Ce lien (également mentionné précédemment) fournissait 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 susceptible de causer des problèmes similaires. Vous pouvez également déplacer celui-ci.

Dans ce cas, la première étape consiste à vérifier si le GC 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, il est défini sur 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. Par conséquent, 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

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

La solution correcte 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’enregistrement des pilotes (JDBC) n’a pas réussi, 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 de 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" ont tort car j’exécute la version 6.0.29 et 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 me redéployer 3 fois avant de générer l’erreur au lieu de chaque redéploiement.

Si vous obtenez cela dans l'IDE Eclipse, même après avoir défini les paramètres - launcher.XXMaxPermSize , -XX: MaxPermSize , etc., même si vous obtenez la même erreur, il est fort probable que l'éclipse utilise une version boguée de JRE qui aurait été installé par certaines applications tierces et défini 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. 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 de l'éclipse sur la version correcte de java.

La seule méthode qui a fonctionné pour moi a été 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 Java en cours d'exécution. 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.

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 dans Google GUICE.

Chaque fois que j'essayais d'exécuter la commande du package correct mvn , le message d'erreur suivant apparaissait et "BUILD FAILURE" se produisait

.
  

org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; exception imbriquée est java.lang.reflect.InvocationTargetException: null   java.lang.reflect.InvocationTargetException   Causée par: java.lang.OutOfMemoryError: espace PermGen

J'ai essayé tous les trucs et astuces 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. Accédez à votre pom.xml
  2. Recherchez < artifactId > maven-surefire-plugin < / artifactId >
  3. Ajoutez un nouvel élément < configuration > , puis < argLine > dans lequel passe -Xmx512m -XX: MaxPermSize = 256m comme indiqué ci-dessous = >

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top