Fuite de mémoire lors du redéploiement de L'application dans Tomcat
J'ai WebApplication qui est déployé dans Tomcat 7.0.70. J'ai simulé la situation suivante:
- j'ai créé le tas de déchets.
- puis j'ai envoyé la demande Http et dans la méthode de service j'ai imprimé le thread courant et son classLoader. Et puis j'ai invoqué Fil.currentThread.sommeil (10000).
- et au même moment j'ai cliqué sur "undeploy this application" dans la page d'administration de Tomcat.
- I créé de nouveaux tas de vidage.
- après quelques minutes, j'ai créé un nouveau dump hep.
résultats
filetage
sur l'écran suivant, vous pouvez voir qu'après que j'ai cliqué sur "redéployer", tous les threads (qui étaient associés à cette application web) ont été tués sauf le thread "http-avr-8081-exec-10". Comme J'ai défini L'attribut de Tomcat "renewThreadsWhenStoppingContext = = true", de sorte que vous pouvez voir qu'après un certain temps ce fil ("http-apr-8081-exec-10") a été tué et nouveau fil (http-apr-8081-exec-11) a été créé à sa place. Donc je ne m'attendais pas à avoir l'ancienne WCL après la création de tas dump 3, parce qu'il n'y a pas de vieux fils ou objets.
Heapd dump 1
Sur les deux écrans, vous pouvez voir que lorsque l'application était en cours d'exécution il n'y avait qu'un CMT(paramètre "démarrage" = true). Et le thread "http-apr-8081-exec-10" avait le contextClassLoader = URLClassLoader ( parce qu'il était dans la piscine de Tomcat). Je ne parle que de ce thread car vous verrez que ce thread traitera ma future requête HTTP.
envoyer une demande HTTP
maintenant j'envoie la requête HTTP et dans mon code je reçois des informations sur le thread courant.Vous pouvez voir que ma requête est traitée par le thread "http-apr-8081-exec-10"
дек 23, 2016 9:28:16 AM c.c.c.f.s.r.ReportGenerationServiceImpl INFO: request has been handled in
thread = http-apr-8081-exec-10, its contextClassLoader = WebappClassLoader
context: /hdi
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader: java.net.URLClassLoader@4162ca06
puis je clique sur "redéployer mon application web" et j'obtiens le message suivant dans la console.
дек 23, 2016 9:28:27 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
SEVERE: The web application [/hdi] appears to have started a thread named [http-apr-8081-exec-10] but has failed to stop it. This is very likely to create a memory leak.
heapd dump 2
sur les écrans suivants vous pouvez voir qu'il y a deux instances WebAppClassLoader. L'un d'eux( numéro 1) est ancien (son attribut "commencé" = false). Et la WCL #2 a été créée après le redéploiement de l'application (son attribut "start" = true). Et le fil que nous examinons a contextClassLoader = "org.Apache.Catalina.chargeur.WebappClassLoader". Pourquoi? Je m'attendais pour voir contextClassLoader = " java.net.URLClassLoader" (après tout, lorsque n'importe quel thread termine son travail, il est retourné à la piscine du Tomcat et son attribut "contextClassLoader" est défini à n'importe quel classloader de base).
heapd dump 3
Vous pouvez voir qu'il n'y a pas de thread "http-avr-8081-exec-10", mais il n'y a thread "http-avr-8081-exec-11" et il a contextClassLoader = "WebappClassLoader"
(Pourquoi pas URLClassLoader?).
à la fin nous avons le suivant: il y a le fil" http-apr-8081-exec-11 " qui a la réf à la WebappClassLoader #1. Et évidemment quand je fais" la racine GC la plus proche " sur la WCL #1 je vais voir la ref sur le fil 11.
Questions.
Comment puis-je forcer Tomcat à retourner les anciennes valeurs contextClassLoader (URLClassLoader) après que thread aura terminé son travail?
Comment puis-je m'assurer que Tomcat ne copie pas les anciennes valeurs "contextClassLoader" pendant le renouvellement du thread?
Peut-être, connaissez-vous d'autres façon de résoudre mon problème?
6 réponses
Tomcat n'est généralement pas une bonne option sur les environnements de production. J'utilisais Tomcat sur quelques applications de production et j'ai découvert que même si la taille du tas et d'autres configurations sont correctement configurées - et chaque fois que vous rechargez votre application, la consommation de mémoire augmente. Tant que vous ne redémarrez pas le service tomcat, la mémoire n'est pas entièrement récupérée. Nous avons testé toutes ces expériences, comme Effacer les journaux, redéployer toutes les applications, redémarrer tomcat régulièrement une fois par mois ou un semaine pendant moins occupé heures. Mais en fin de compte, je dois dire que nous avons réorienté nos environnements de production vers la Glassfish et la WebSphere.
j'espère que vous avez déjà parcouru ces pages:
fuite de mémoire dans une application Web Java
Tomcat Réparer Une Fuite De Mémoire?
https://developers.redhat.com/blog/2014/08/14/find-fix-memory-leaks-java-application /
http://www.tomcatexpert.com/blog/2010/04/06/tomcats-new-memory-leak-prevention-and-detection
si vos applications web ne sont pas étroitement couplées avec Tomcat, alors vous pouvez penser à utiliser un autre conteneur web. Maintenant, nous utilisons le Glassfish même sur les machines de développement et de production et le jour où nous prenons cette décision, nous avons sauvé beaucoup de notre temps. Bien que Glassfish et d'autres serveurs de ce type prennent plus de temps alors qu'ils commencent car ils ne sont pas aussi légers que le Tomcat est, mais après la vie est un peu plus facile.
d'après mon expérience avec ce problème, ce qui empêchait tomcat De correctement GC Chargeurs de classe plus âgés était certains ThreadLocal
s un couple de cadres que j'employais créaient (et ne pas manipuler correctement).
quelque chose de similaire à ce qui est expliqué ici: ThreadLocal & memory Leak
j'ai essayé de finaliser correctement ce ThreadLocal
s et ma fuite réduit beaucoup. Il fuyait encore, mais je pouvais supporter 10 fois plus. redéploie qu'avant.
je vérifierais certainement votre mémoire vide vers des objets qui pourraient être connectés d'une manière ou d'une autre à ThreadLocal
s (ils sont très fréquents, surtout si vous utilisez quelque chose pour contrôler les transactions ou tout ce qui est filé-isolé).
j'espère que cela aide!
fuite de mémoire dans le redéploiement de tomcat est un très vieux problème. La seule véritable façon de le résoudre est de redémarrer tomcat au lieu de redéployer l'application. Si vous avez plusieurs applications, vous avez besoin d'exécuter plusieurs services tomcat sur différents ports et de le rejoindre avec nginx.
nous avons des centaines D'instances Tomcat en cours d'exécution dans plusieurs environnements (également la production) et la seule solution raisonnable que nous avons trouvée à ce problème est d'arrêter et de redémarrer chaque Tomcat à une heure définie daily (dans la nuit).
nous avons essayé de nombreux trucs, mais c'est la solution durable pour nos besoins de temps de disponibilité.
Tomcat n'est généralement pas une bonne option sur les environnements de production. J'utilisais Tomcat sur quelques applications de production et j'ai découvert que même si la taille du tas et d'autres configurations sont correctement configurées - et chaque fois que vous rechargez votre application, la consommation de mémoire augmente. Tant que vous ne redémarrez pas le service tomcat, la mémoire n'est pas entièrement récupérée. Nous avons testé toutes ces expériences, comme Effacer les journaux, redéployer toutes les applications, redémarrer tomcat régulièrement une fois par mois ou un semaine pendant moins occupé heures. Mais en fin de compte, je dois dire que nous avons réorienté nos environnements de production vers la Glassfish et la WebSphere.
vérifiez les utilisations locales qui empêchent votre ClassLoader d'être ramassée. Soit supprimer les références à vos classes dans ThreadLocal values, soit utiliser https://github.com/codesinthedark/ImprovedThreadLocal au lieu de ThreadLocal