首页 > 解决方案 > Tomcat 9 错误 - 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 JVM:java-8-openjdk-amd64 Mysql 驱动程序:mysql-connector-java-8.0.20

服务器.xml

 driverClassName="com.mysql.jdbc.Driver"

我试图将 server.xml 更改为

com.mysql.cj.jdbc.Driver

但在 Catalina.out 中仍有警告。

有解决问题的指南吗?

谢谢你。

标签: javatomcatmysql-connector

解决方案


如果您的 Web 应用程序在文件夹中有 MySQL JDBC 驱动程序的副本,则 Tomcat 中使用的类加载器WEB-INF/lib的特殊委托规则将选择 Web 应用程序的驱动程序副本而不是全局驱动程序副本。

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

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

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

备注:即使您不DriverManager直接使用,但某些数据库池库(最终将使用DriverManager)或者如果您<Resource><Context>. 只有在 中<Resource>定义的情况<GlobalNamingResources>不受影响。

要解决问题,您可以:

  • 从您的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);
         }
      }
   }
}

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


推荐阅读