Tomcat 7强制注销了JDBC Driver,为啥?

Posted

技术标签:

【中文标题】Tomcat 7强制注销了JDBC Driver,为啥?【英文标题】:JDBC Driver has been forcibly unregistered by Tomcat 7, why?Tomcat 7强制注销了JDBC Driver,为什么? 【发布时间】:2013-10-11 22:18:19 【问题描述】:

我在 tomcat 7 中遇到问题,这里有一些关于它的信息,

1 - 我收到这条消息:

INFO: Reloading Context with name [/WebApp] has started
Oct 04, 2013 12:20:50 PM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/WebApp] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

Oct 04, 2013 12:20:50 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/WebApp] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak.
Oct 04, 2013 12:20:51 PM org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/WebApp] is completed

2 - 当我重新加载应用程序时,问题解决了大约 20 小时,然后又回来了。

3 - 我在 tomcat 上部署了大约 10 个应用程序,但其中只有 2 个出现此错误。

4 - 这两个应用程序的乞讨不存在问题,但从大约 2 周开始出现。

那么我该如何解决这个问题,它与我的代码有关吗?

【问题讨论】:

因为理想情况下,驱动程序应该位于 Tomcat 的类路径中,而不是 webapp 的。如果驱动程序在 webapp 的类路径中,那么不取消注册将导致 perm-gen 泄漏,因为无法卸载 ClassLoader @Boris Tomcat 的类路径和应用程序的类路径没有区别。这是一个单一的java 进程。 @Boris 好的,我会假设,但正如我所说,我部署了 10 个应用程序,并且都将驱动程序添加到了它的路径中,那么为什么所有应用程序都没有出现错误? @SotiriosDelimanolis 别傻了。如果您阅读 this 可能会有所帮助。简而言之,每个 webapp 都有自己的类路径,由 webapp 中的 lib 文件夹组成。 @SotiriosDelimanolis 您只是在争论语义,尽管使用术语“类路径”在所有方面都是一个糟糕的选择。如果您将术语“类路径”替换为“类加载器”,那么除了您挑剔的 cmets 之外,一切都很好。与其简单地告诉人们他们错了(而且没有丝毫帮助),不如改正术语。 【参考方案1】:

当您在 Tomcat 中停止 Web 应用程序时,它会尝试关闭它启动的线程并关闭一堆资源,例如 JDBC 驱动程序。虽然在这种情况下它可以关闭它们,但自己做会更安全。

您可以在ServletContextListener 中执行此操作。我的实现如下

@WebListener // register it as you wish
public class ContainerContextClosedHandler implements ServletContextListener 
    private static final Logger logger = LoggerFactory.getLogger(ContainerContextClosedHandler.class);

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) 
        // nothing to do
    

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) 
        Enumeration<Driver> drivers = DriverManager.getDrivers();     

        Driver driver = null;

        // clear drivers
        while(drivers.hasMoreElements()) 
            try 
                driver = drivers.nextElement();
                DriverManager.deregisterDriver(driver);

             catch (SQLException ex) 
                // deregistration failed, might want to do something, log at the very least
            
        

        // MySQL driver leaves around a thread. This static method cleans it up.
        try 
            AbandonedConnectionCleanupThread.shutdown();
         catch (InterruptedException e) 
            // again failure, not much you can do
        
    


MySQL 确实启动了一个 Tomcat 无法关闭的线程。对于当前版本(5.1.23+),他们提供了AbandonedConnectionCleanupThread 类来关闭生成的Thread,如上所示。

【讨论】:

+1。 Collections.list(DriverManager.getDrivers()).forEach(d-&gt;try DriverManager.deregisterDriver(d);catch(final SQLException sqle)); 为避免此类问题,您可以在服务器上使用数据库连接池。 Glassfish + HyperSQL 也有同样的问题。【参考方案2】:

如果您在每个 web 应用的 WEB-INF/lib 目录中都有您的 Connector/J JDBC 驱动程序,那么您的所有 web 应用都可能会遇到类似的问题——不仅仅是这个。

如果您使用的是 Tomcat 的 JDBC 连接池,那么您应该将 Connector/J 驱动程序放入 Tomcat 的 lib/ 目录中,并将其从您的所有 webapps 中删除。如果您在自己的应用程序中维护自己的连接池,则必须安排 JDBC 驱动程序在全局 DriverManager 中注销自身。更好的是,使用 Connector/J 的非注册驱动程序而不是注册驱动程序,这样您就不必担心 Tomcat 实际上可以保护您免受的这些类型的泄漏。

【讨论】:

假设你在 Tomcat 的配置中声明了一个资源,并且没有实时的 Web 应用程序正在使用它,Tomcat 会关闭它吗? Tomcat 在启动时创建资源,但最初不建立任何连接。如果您从不使用它,它将使用最少的资源。 IIRC,一旦你使用它,这些连接将无限期地保持打开状态,即使使用它们的 webapp 未部署。您可以为连接设置“空闲超时”,然后它们将在该时间间隔后关闭。 在这种情况下,您仍然需要在由 MySQL JDBC 驱动程序启动的 AbandonedConnectionCleanupThread 上调用 shutdown() @SotiriosDelimanolis 是的,但这是 MySQL 连接器/J 特有的东西。我一直在向 MySQL 抱怨他们的驱动程序在ClassLoader、线程等方面做了一些愚蠢的事情,但他们似乎并不在意。我们必须解决这种愚蠢的问题,才能彻底取消部署 web 应用程序。

以上是关于Tomcat 7强制注销了JDBC Driver,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

无法加载 JDBC 驱动程序类“com.mysql.jdbc.Driver”Tomcat 8 和 Eclipse

java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

GlassFish 为防止内存泄漏,已强制注销 JDBC 驱动程序

无法在 Tomcat 中加载 net.sourceforge.jtds.jdbc.Driver

注册了JDBC驱动程序 [com.alibaba.druid.proxy.DruidDriver],但在Web应用程序停止时无法注销它。 为防止内存泄漏,JDBC驱动程序已被强制取消注册。

注册了JDBC驱动程序 [com.alibaba.druid.proxy.DruidDriver],但在Web应用程序停止时无法注销它。 为防止内存泄漏,JDBC驱动程序已被强制取消注册。