webentwicklung-frage-antwort-db.com.de

Speicherverlust beim erneuten Bereitstellen der Anwendung in Tomcat

Ich habe eine Webanwendung, die in Tomcat 7.0.70 implementiert ist. Ich habe die folgende Situation simuliert:

  1. Ich habe den Heap-Dump erstellt.
  2. Dann schickte ich die Http-Anfrage und in der Service-Methode druckte ich den aktuellen Thread und seinen classLoader. Und dann habe ich Thread.currentThread.sleep (10000) aufgerufen.
  3. Und im selben Moment habe ich auf der Admin-Seite von Tomcat auf "Diese Anwendung nicht mehr bereitstellen" geklickt.
  4. Ich habe einen neuen Heap-Dump erstellt.
  5. Nach einigen Minuten habe ich einen neuen Hep-Dump erstellt.


ERGEBNISSE


Thread-Dump

Auf dem folgenden Bildschirm können Sie sehen, dass nach dem Klicken auf "erneut implementieren" alle Threads (die dieser Webanwendung zugeordnet waren) mit Ausnahme des Threads "http-apr-8081-exec-10" beendet wurden. Da ich das Tomcat-Attribut "renewThreadsWhenStoppingContext == true" gesetzt habe, können Sie sehen, dass dieser Thread ("http-apr-8081-exec-10") nach einiger Zeit beendet wurde und ein neuer Thread (http-apr-8081-exec-11) beendet wurde ) wurde stattdessen erstellt. Ich hatte also nicht damit gerechnet, die alte WCL nach der Erstellung von Heap Dump 3 zu haben, da es keine alten Threads oder Objekte gibt.

 enter image description here

Heapd Dump 1

Auf den folgenden zwei Bildschirmen können Sie sehen, dass es beim Ausführen der Anwendung nur eine WCL gab (der Parameter "started" = true). . Der Thread "http-apr-8081-exec-10" hatte die contextClassLoader = URLClassLoader (weil es sich im Tomcat-Pool befand). Ich spreche nur über diesen Thread, da Sie sehen können, dass dieser Thread meine zukünftige HTTP-Anforderung verarbeitet.

 enter image description here

 enter image description here

HTTP-Anfrage senden

Nun sende ich die HTTP-Anfrage und erhalte in meinem Code Informationen über den aktuellen Thread. Sie können sehen, dass meine Anfrage vom Thread "http-apr-8081-exec-10" verarbeitet wird.

дек 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: [email protected]

Dann klicke ich auf "Web-Anwendung erneut bereitstellen" und erhalte folgende Meldung in der Konsole.

 дек 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

In den folgenden Bildschirmen sehen Sie, dass es zwei Instanzen gibt: WebAppClassLoader. Einer von ihnen (Nummer # 1) ist alt (sein Attribut "started" = false). Und die WCL # 2 wurde erstellt, nachdem die Anwendung erneut implementiert wurde (ihr Attribut "started" = true). Und der Thread, den wir überprüfen, hat contextClassLoader = "org.Apache.catalina.loader.WebappClassLoader". Warum? Ich hatte erwartet, contextClassLoader = "Java.net.URLClassLoader" zu sehen (schließlich wird ein Thread nach Beendigung seiner Arbeit an den Pool Des Tomcat zurückgegeben und sein Attribut "contextClassLoader" wird auf einen beliebigen Basisklassenlader gesetzt).

 enter image description here

 enter image description here

 enter image description here

Heapd-Dump 3

Sie sehen, dass es keinen Thread "http-apr-8081-exec-10" gibt, aber es gibt den Thread "http-apr-8081-exec-11" und es hat contextClassLoader = "WebappClassLoader" ( Warum nicht URLClassLoader?).

Am Ende haben wir folgendes: Es gibt den Thread "http-apr-8081-exec-11", der den Verweis auf den WebappClassLoader # 1 hat. Und wenn ich "Nearest GC Root" auf der WCL # 1 mache, sehe ich den Hinweis auf den Thread 11.

 enter image description here

 enter image description here

Fragen.

Wie kann ich Tomcat zwingen, den alten Wert contextClassLoader (URLClassLoader) zurückzugeben, nachdem der Thread seine Arbeit beendet hat?

Wie kann ich sicherstellen, dass Tomcat während der Thread-Erneuerung keinen alten Wert "contextClassLoader" kopiert?

Wissen Sie vielleicht einen anderen Weg, um mein Problem zu lösen?

21
Konstantin B.

Tomcat ist in Produktionsumgebungen normalerweise keine gute Option. Ich habe Tomcat für ein paar Produktionsanwendungen verwendet, und ich habe festgestellt, dass selbst wenn die Größe des Heapspeichers und andere Konfigurationen ordnungsgemäß eingerichtet sind - und jedes Mal, wenn Sie Ihre Anwendung neu laden, der Speicherverbrauch immer höher wird. Bis Sie den Tomcat-Dienst nicht neu starten, wird der Speicher nicht vollständig freigegeben. Wir haben alle diese Experimente getestet, wie zum Beispiel Protokolle gelöscht, alle Apps erneut bereitgestellt und Tomcat regelmäßig einmal im Monat oder in einer Woche zu den am wenigsten beanspruchten Stunden neu gestartet. Aber am Ende muss ich sagen, dass wir unsere Produktionsumgebungen auf Glassfish und WebSphere verlagert haben. 

Ich hoffe, Sie hätten diese Seiten bereits durchlaufen:

Speicherverlust in einer Java-Webanwendung

Tomcat Fix Speicherleck?

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

Wenn Ihre Webanwendungen nicht eng mit Tomcat gekoppelt sind, können Sie einen anderen Webcontainer verwenden. Jetzt setzen wir den Glassfish sogar auf Entwicklungsmaschinen und in der Produktion ein. An dem Tag, an dem wir diese Entscheidung treffen, haben wir viel Zeit gespart. Glassfish und andere Server benötigen zwar mehr Zeit, da sie nicht so leicht sind wie der Tomcat, aber das Leben nach dem Leben ist etwas einfacher.

3
Naveed Kamran

Aus meiner Erfahrung mit diesem Problem: Was Tomcat daran hinderte, ältere Klassenlader ordnungsgemäß zu GC-fähig zu machen, waren einige ThreadLocals, die von einigen Frameworks erstellt wurden (und nicht ordnungsgemäß gehandhabt wurden).

Ähnliches, was hier erklärt wird: ThreadLocal & Memory Leak

Ich habe versucht, diese ThreadLocals richtig zu finalisieren, und mein Leck hat das VIEL reduziert. Es war immer noch undicht, aber ich konnte zehnmal mehr Umschichtungen durchführen als zuvor.

Ich würde definitiv Ihre Speicherabbilder auf Objekte überprüfen, die irgendwie mit ThreadLocals verbunden sein könnten (sie sind sehr üblich, insbesondere wenn Sie etwas zur Steuerung von Transaktionen oder etwas verwenden, das Thread-isoliert ist).

Ich hoffe, es hilft!

1
BrunoJCM

Tomcat ist in Produktionsumgebungen normalerweise keine gute Option. Ich habe Tomcat für ein paar Produktionsanwendungen verwendet, und ich habe festgestellt, dass selbst wenn die Größe des Heapspeichers und andere Konfigurationen ordnungsgemäß eingerichtet sind - und jedes Mal, wenn Sie Ihre Anwendung neu laden, der Speicherverbrauch immer höher wird. Bis Sie den Tomcat-Dienst nicht neu starten, wird der Speicher nicht vollständig freigegeben. Wir haben alle diese Experimente getestet, wie zum Beispiel Protokolle gelöscht, alle Apps erneut bereitgestellt und Tomcat regelmäßig einmal im Monat oder in einer Woche zu den am wenigsten beanspruchten Stunden neu gestartet. Aber am Ende muss ich sagen, dass wir unsere Produktionsumgebungen auf Glassfish und WebSphere verlagert haben.

0
Wael Mofreh

Ein Speicherleck bei Tomcat's Neuverwendung ist ein sehr altes Problem. Der einzige Weg, um das Problem zu lösen, ist, Tomcat neu zu starten, anstatt die Anwendung erneut zu implementieren. Wenn Sie über mehrere Apps verfügen, müssen Sie mehrere Tomcat-Dienste an verschiedenen Ports ausführen und sich mit nginx verbinden.

0
A.Alexander

Suchen Sie nach ThreadLocal-Verwendungen, die verhindern, dass Ihr ClassLoader Müll sammelt. Entfernen Sie entweder Verweise auf Ihre Klassen in ThreadLocal-Werten oder verwenden Sie https://github.com/codesinthedark/ImprovedThreadLocal anstelle von ThreadLocal

0
CodesInTheDark

Wir haben Hunderte von Tomcat-Instanzen, die in mehreren Umgebungen (auch in der Produktion) ausgeführt werden. Die einzige vernünftige Lösung, die wir für dieses Problem gefunden haben, besteht darin, jeden Tomcat zu einer bestimmten Zeit zu stoppen und erneut zu starten daily (in der Nacht).

Wir haben viele Tricks ausprobiert, aber dies ist die dauerhafte Lösung für unsere Verfügbarkeitsanforderungen.

0
Marc.S