Tomcat 9 错误 - mysql-cj-abandoned-connection-cleanup

Tomcat 9 error - mysql-cj-abandoned-connection-cleanup

我有一个程序 运行 在 Tomcat 9.

当我重启问题时,显示上面的警告:

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 版本:Tomcat 9 虚拟机:java-8-openjdk-amd64 Mysql 驱动程序:mysql-连接器-java-8.0.20

server.xml

 driverClassName="com.mysql.jdbc.Driver"

我试过把server.xml改成

com.mysql.cj.jdbc.Driver

但是Catalina.out中仍然有警告。

有解决问题的指南吗?

谢谢。

如果您的 Web 应用程序在 WEB-INF/lib 文件夹中有 MySQL JDBC 驱动程序的副本,则 Tomcat 中使用的特殊 delegation rules for classloaders 将 select Web 应用程序的驱动程序副本,而不是全局驱动程序副本。

这将在 bootstrap 类加载器中创建对应用程序类加载器的两个引用:

  • 驱动程序将在 DriverManager 中注册(在 bootstrap 类加载器中),
  • 驱动程序将创建一个新线程,ContextClassLoader 设置为 webapp 类加载器。

两个引用都可能造成内存泄漏。

备注: 即使您不直接使用 DriverManager 也会发生这种情况,但是某些数据库池库(将在end) 或者如果您在 <Context> 中定义了 JNDI <Resource>。只有在 <GlobalNamingResources> 中定义的 <Resource> 的情况不受影响。

解决问题你可以:

  • 要么从您的 WEB-INF/lib 目录中删除数据库驱动程序,
  • 当应用程序通过调用 DriverManager.deregister 停止时撤消这些更改,例如在 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);
         }
      }
   }
}

编辑:我将评论中的信息合并到答案中。