web-dev-qa-db-fra.com

Fuite de mémoire lors du redéploiement de l'application dans Tomcat

Lorsque je redéploie mon application dans Tomcat, j'obtiens le problème suivant:

 The web application [] created a ThreadLocal with key of type
 [Java.lang.ThreadLocal] (value [Java.lang.ThreadLocal@10d16b])
 and a value of type [com.Sun.xml.bind.v2.runtime.property.SingleElementLeafProperty]
(value [com.Sun.xml.bind.v2.runtime.property.SingleElementLeafProperty@1a183d2]) but 
 failed to remove it when the web application was stopped. 
 This is very likely to create a memory leak.

J'utilise également ehcache dans mon application. Cela semble également entraîner l'exception suivante.

     SEVERE: The web application [] created a ThreadLocal with key of type [null] 
     (value [com.Sun.xml.bind.v2.ClassFactory$1@24cdc7]) and a value of type [Java
     .util.WeakHashMap... 

Le ehcache semble créer une carte de hachage faible et je reçois le message que cela est très susceptible de créer une fuite de mémoire.

J'ai cherché sur le net et j'ai trouvé cela, http://jira.pentaho.com/browse/PRD-3616 mais je n'ai pas accès au serveur en tant que tel.

Veuillez me faire savoir si ces avertissements ont un impact fonctionnel ou peuvent-ils être ignorés? J'ai utilisé l'option "Rechercher les fuites de mémoire" dans le gestionnaire Tomcat et il indique "Aucune fuite de mémoire trouvée"

45
Raghav

Lorsque vous redéployez votre application, Tomcat crée un nouveau chargeur de classe. L'ancien chargeur de classe doit être récupéré, sinon vous obtenez une fuite de mémoire permgen.

Tomcat ne peut pas vérifier si le garbage collection fonctionnera ou non, mais il connaît plusieurs points communs d'échecs. Si le chargeur de classe webapp définit un ThreadLocal avec une instance dont la classe a été chargée par le chargeur de classe webapp lui-même, le thread de servlet contient une référence à cette instance. Cela signifie que le chargeur de classe ne sera pas récupéré.

Tomcat effectue un certain nombre de ces détections, voir ici pour plus d'informations . Le nettoyage des sections locales des threads est difficile, vous devez appeler remove() sur le ThreadLocal dans chacun des threads auxquels vous avez accédé. En pratique, cela n'est important que lors du développement lorsque vous redéployez votre application Web plusieurs fois. En production, vous ne redéployez probablement pas, cela peut donc être ignoré.

Pour vraiment savoir quelles instances définissent les sections locales des threads, vous devez utiliser un profileur. Par exemple, le marcheur de tas dans JProfiler (avertissement: ma société développe JProfiler) vous aidera à trouver ces sections locales de thread. Sélectionnez la classe de valeurs signalée (com.Sun.xml.bind.v2.runtime.property.SingleElementLeafProperty ou com.Sun.xml.bind.v2.ClassFactory) et affichez les références entrantes cumulées. L'un d'eux sera un Java.lang.ThreadLocal$ThreadLocalMap$Entry. Sélectionnez les objets référencés pour ce type de référence entrant et basculez vers la vue des allocations. Vous verrez où l'instance a été allouée. Avec ces informations, vous pouvez décider si vous pouvez faire quelque chose ou non.

enter image description here

40
Ingo Kegel

Mattias Jiderhamn a un excellent article en 6 parties qui explique très clairement la théorie et la pratique des fuites du chargeur de classe. Encore mieux, il a également publié un fichier jar que nous pouvons inclure dans nos fichiers de guerre. Je l'ai essayé sur mes applications Web et le fichier jar a fonctionné comme un charme! Le fichier jar est appelé classloader-leak-prevention.jar. Pour l'utiliser, c'est aussi simple que de l'ajouter à notre web.xml

<listener>
  <listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class>
</listener>

puis l'ajouter à notre pom.xml

<dependency>
  <groupId>se.jiderhamn</groupId>
  <artifactId>classloader-leak-prevention</artifactId>
  <version>1.15.2</version>
</dependency>

Pour plus d'informations, veuillez vous référer à la page d'accueil du projet hébergée sur GitHub ou Partie 6 de son article

7
leeyuiwah

Créer des threads sans les nettoyer correctement finira par vous faire manquer de mémoire - vous y êtes, faites-le.

Ceux qui se demandent encore une solution/solution rapide peuvent aller ci-dessous:

  • Si vous exécutez le Tomcat autonome, supprimez javaw.exe ou le processus qui le porte.
  • Si vous exécutez depuis Eclipse, supprimez Eclipse.exe et Java.exe ou le processus englobant.
  • Toujours pas résolu, vérifiez le gestionnaire de tâches, il est probable que le processus qui provoque cela soit affiché avec la plus grande utilisation de la mémoire - Faites votre analyse et tuez cela.

Vous devriez être bon de redéployer le truc et de continuer sans problèmes de mémoire.

4
Snehal Masne

Je suppose que vous avez probablement vu cela, mais juste au cas où le doc ehcache recommande de mettre la lib dans Tomcat et non dans WEB-INF/lib .

2
user2177336

Je recommande d'initialiser sections locales de threads , dans un ServletRequestListener .

ServletRequestListener a 2 méthodes: une pour l'initialisation et une pour la destruction.

De cette façon, vous pouvez nettoyer votre ThreadLocal. Exemple:

public class ContextInitiator implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        context = new ThreadLocal<ContextThreadLocal>() {
            @Override
            protected ContextThreadLocal initialValue() {
                ContextThreadLocal context = new ContextThreadLocal();
                return context;
            }
        };
        context.get().setRequest(sre.getServletRequest());
    }
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        context.remove();
    }
}

web.xml:

<listener>
    <listener-class>ContextInitiator</listener-class>
</listener>
1