Bug #68556 | Tomcat can't stop a cleanup thread by clearReferencesStopThreads. | ||
---|---|---|---|
Submitted: | 4 Mar 2013 7:29 | Modified: | 15 Apr 2013 0:28 |
Reporter: | Sadao Hiratsuka | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | Connector / J | Severity: | S3 (Non-critical) |
Version: | 5.1.23 | OS: | Any |
Assigned to: | Alexander Soklakov | CPU Architecture: | Any |
Tags: | regression |
[4 Mar 2013 7:29]
Sadao Hiratsuka
[6 Mar 2013 9:07]
Alexander Soklakov
Hi Sadao, Thanks for wonderful catch! 3-1 is really much better, we should fix it.
[12 Apr 2013 14:07]
Bugs System
Added changelog entry for 5.1.25: "Tomcat would fail to stop an abandoned connection cleanup thread." Please advise if this entry requires revision.
[15 Apr 2013 0:28]
Bugs System
The 5.1.25 changelog entry has been revised as follows: "Tomcat would fail to stop abandoned connection cleanup threads. The fix for Bug#65909 introduced the ability to stop daemon threads started by the Connector/J driver but it also cleared references from daemon threads to the parent classloader. When the "clearReferencesStopThreads" property is set to "true" in "context.xml", Tomcat analyzes classloaders to detect and stop lost threads. This fix ensures that abandoned connection cleanup threads retain a reference to the parent classloader."
[31 May 2013 20:06]
Christopher Schultz
This is a terrible fix for this bug, for a number of reasons. First, by retaining the current contextClassLoader, you bind the thread to the WebappClassLoader and then need to resort to the kinds of hacks provided by Tomcat to fix the problem. It would be much better to fix the problem at its root. Second, when Tomcat kills the AbandonedConnectionCleanupThread, it will never run again, so reloading a webapp will effectively kill this thread for the life of the JVM (for a standard Tomcat configuration where Connector/J is actually loaded from the container's ClassLoader and not the WebappClassLoader). Third, the thread is launched from a static initializer in NonRegisteringDriver, like this: static { AbandonedConnectionCleanupThread referenceThread = new AbandonedConnectionCleanupThread(); referenceThread.setDaemon(true); referenceThread.start(); } This means that there are no references to the thread in order to stop it nicely. A better fix would be to: 1. Use a (private, probably) static member reference in NonRegisteringDriver to retain a reference to the thread. 2. Provide a static method in NonRegisteringDriver to shut down the thread. 3. Re-start the thread whenever appropriate. 4. Set the contextClassLoader of the thread *that creates the AbandonedConnectionCleanupThread* to be the ClassLoader that loaded NonRegisteringDriver *before* creating the AbandonedConnectionCleanupThread. That last bit is fairly important, as it fixes a related leak that can't be prevented in another way: where Thread.inheritedAccessControlContext has references to the WebappClassLoader that are inherited from the context ClassLoader when the Thread is created (before any Connector/J code runs for the thread). Please re-examine this bugfix and consider my suggestions. Otherwise, Connector/J will either continue to leak ClassLoaders on web application reloads, or this thread will be terminated on the first reload and never start again, significantly limiting its usefulness.
[5 Jun 2013 17:12]
Christopher Schultz
Here is a much better workaround until something else changes. Enable Tomcat's JreMemoryLeakPreventionListener (enabled by default on Tomcat 7), and add this attribute to the <Listener> element: classesToInitialize="com.mysql.jdbc.NonRegisteringDriver" If "classesToInitialize" is already set on your <Listener>, just add NonRegisteringDriver to the existing value separated by a comma.
[8 Jun 2013 21:33]
Marko Asplund
I did some testing with the JreMemoryLeakPreventionListener / classesToInitialize workaround (Tomcat 7.0.39 + MySQL Connector/J 5.1.25). Before applying the workaround thread dumps listed multiple AbandonedConnectionCleanupThread instances after redeploying the webapp several times. After applying the workaround there's only one AbandonedConnectionCleanupThread instance. I had to modify my app, though, and move MySQL driver from the webapp to Tomcat lib. Otherwise, the classloader is unable to load com.mysql.jdbc.NonRegisteringDriver at Tomcat startup.