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->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驱动程序已被强制取消注册。