Tomcat - MemoryLeakDetection

Hi!

Ich habe seit kurzem einen Tomcat 8.0.20 im Einsatz und ein paar Probleme mit der MemoryLeakDetection.

In meiner App habe ich einen Scheduler. Es ist ein TimerTask welcher in der initMethode eines Servlets einmalig gestartet wird. Dieser holt sich eine DbConnection aus einem Pool. Liest ein paar kleine Daten und schießt die Connection im FinallyBlock. Das untenstehende ist wohl nur eine Warnung, ohne dass sonst etwas passiert?

16-Mar-2015 17:48:42.328 WARNING [http-apr-8080-exec-8] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [webapp] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:

Bedeutet das untenstehende, das mir Tomcat den Scheduler, also nur den TimeThread, selbsttägig gekillt hat?

16-Mar-2015 17:49:22.983 INFO [Timer-0] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [org.apache.log4j.spi.LocationInfo]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
 java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.apache.log4j.spi.LocationInfo]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.

Bedeutet das hier, dass der Scheduler nicht mehr existiert?

Exception in thread "Timer-0" java.lang.IllegalStateException: Can't overwrite cause with java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.apache.log4j.spi.LocationInfo]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.

Der Scheduler läuft trotzdem irgendwie weiter, heißt immer Timer-0 und kommt es öfter vor, dass sich der Scheduler keine Connection erhält und die untenstehende Log kommt. Interessant ist, dass der Scheduler, während des Betriebs, mal eine Connection bekommt und dann mal wieder nicht.

2015-03-16 18:01:41 /webapp ERROR Scheduler:113 - Name [comp/env/jdbc/myDS] is not bound in this Context. Unable to find [comp].

Wo könnte das Problem liegen?
Wo soll ich genauer hinsehen?

Danke!

Alle geposteten Logs deuten darauf hin, dass Dein Vorgehen ein Problem werden KÖNNTE. Es läuft eben ein Thread völlig unabhängig vom Lebenszyklus der Webapplikation. Was passiert bei einem Context Stop? Der Thread läuft noch, Tomcat muss ihn extern killen. Ist es schlimm, wenn er das tut (evtl. noch Resourcen nicht ordentlich geschlossen o.ä.)? Wie sieht es bei einem Context reload aus? usw.

  1. Gewöhne Dir bei der Verwendung von Lebenszyklusmethoden an, auch die “Spiegel”-methode (hier destroy) zu beachten und diese ggf. für Aufräumarbeiten zu nutzen.
  2. Mach Dir nochmal genau Gedanken, ob Servlet init/destroy die geeigneten Zeitpunkte sind. Wann wird init aufgerufen? Gibt es nur eine Instanz Deines Servlets oder mehrere? Wenn es mehrere Instanzen gäbe (was laut Spezifikation nicht auszuschließen ist), hättest Du mehrere Threads. Das sind Dinge, die Du nicht unter Kontrolle hast. Denn die Spezifikation lässt hier einige Freiheitsgrade, die von den Container-Implementierungen (Tomcat, Jetty usw.) möglicherweise unterschiedlich ausgenutzt werden.

Ohne die Spezifika Deines Anwendungsfalles zu kennen, könnte ich mir vorstellen, dass Du mit einem ServletContextListener und dessen contextIntitialized/contextDestroyed Methoden besser fährst.

Wenn EJB’s verfügbar sind kann auch eine EJB in Kombination mit einer @Schedule -Annotation die eine Methode in Regelmässigen Abständen ausführt, sowie einer @Singleton -Annotation damit es nur eine Instanz der EJB gibt, eine vernünftige Lösung sein.

Danke für Eure Antworten.

EJB steht leider nicht zur Verfügung.

Das war mir so noch garnicht bewußt, das der Thread einfach so unabhängig von der WebApp läuft, aber ein paar Experimente haben es bestätigt. Jetzt beende ich den Thread ausdrücklich in der Destroy. Der ServletContextListener wäre sicher auch nicht verkehrt, aber da dieses Servlet niemand aufruft kann nicht viel kaputt gehen.