Reputation: 609
I have a program that running in Tomcat 9.
When I restarted the problem, it shows the above warning:
05-Feb-2021 09:48:34.211 WARNING [Thread-5] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [AWSApps] appears to have started a thread named [mysql-cj-abandoned-connection-cleanup] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
org.apache.catalina.loader.WebappClassLoaderBase.trackLastModified(WebappClassLoaderBase.java:963)
org.apache.catalina.loader.WebappClassLoaderBase.findResource(WebappClassLoaderBase.java:941)
org.apache.catalina.loader.WebappClassLoaderBase.getResource(WebappClassLoaderBase.java:1057)
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.checkThreadContextClassLoader(AbandonedConnectionCleanupThread.java:117)
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:84)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
java.lang.Thread.run(Thread.java:748)
Tomcat Version : Tomcat 9 JVM : java-8-openjdk-amd64 Mysql Driver : mysql-connector-java-8.0.20
server.xml
driverClassName="com.mysql.jdbc.Driver"
I have tried to change the server.xml into
com.mysql.cj.jdbc.Driver
But there is still warning in Catalina.out.
Is there any guide to solve the issue?
Thank you.
Upvotes: 4
Views: 18688
Reputation: 16045
If your web application has a copy of the MySQL JDBC driver in the WEB-INF/lib
folder, the special delegation rules for classloaders used in Tomcat will select the web application's copy of the driver instead of the global one.
That will create two references on your application's classloader in the bootstrap classloader:
DriverManager
(in the bootstrap classloader),ContextClassLoader
set to the webapp classloader.Both references can create a memory leak.
Remark: This happens even if you don't use a DriverManager
directly, but some database pooling library (which will use DriverManager
in the end) or if you define a JNDI <Resource>
in your <Context>
. Only the case of a <Resource>
defined in the <GlobalNamingResources>
is not affected.
To solve the problem you can:
WEB-INF/lib
directory,DriverManager.deregister
, e.g. in a ServletContextListener:public class JdbcDriverListener implements ServletContextListener {
/**
* Deregisters the JDBC drivers distributed with the application.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
final ClassLoader cl = event.getServletContext().getClassLoader();
final Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
final Driver driver = drivers.nextElement();
// We deregister only the classes loaded by this application's classloader
if (driver.getClass().getClassLoader() == cl) {
try {
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
event.getServletContext().log("JDBC Driver deregistration failure.", e);
}
}
}
}
/**
* Registers the JDBC drivers distributed with the application.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
Iterator<@NonNull Driver> driversIterator = ServiceLoader.load(Driver.class).iterator();
while (driversIterator.hasNext()) {
try {
// Instantiates the driver
driversIterator.next();
} catch (Throwable t) {
event.getServletContext().log("JDBC Driver registration failure.", t);
}
}
}
}
Edit: I merge the information from the comments into the answer.
Upvotes: 5