1. 概述
在这个教程中,我们将探讨Tomcat警告消息,它告诉我们它强制注销了一个JDBC驱动器。我们将深入理解这个消息的含义、根本原因以及如何减轻这个问题。
2. 警告信息与含义
警告消息的一个版本可能是这样的:
SEVERE: A web application registered the JBDC driver [oracle.jdbc.driver.OracleDriver]
but failed to unregister it when the web application was stopped.
To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
通过上述信息,Tomcat告知我们,当部署web应用时,JDBC驱动类OracleDriver
被注册,但在卸载相同应用时并未注销。
加载和注册JDBC驱动器的方式有多种,这本质上是一个实现了java.sql.Driver
接口的类。Tomcat使用Java服务提供者接口,并会自动加载在web应用的WEB-INF/lib
目录下找到的任何兼容JDBC 4.0的驱动类。
当我们卸载web应用时,也需要注销它带来的所有驱动。否则,它们将保留在Tomcat中,直到整个Web服务器关闭,这会导致内存泄漏。
自6.0.24版本起,Tomcat检测到此类泄漏并强制注销所有泄漏的驱动。然而,它仍然会通知我们这个问题,这对于我们在不支持此功能的其他Web服务器上重新部署同一应用非常有用。
3. 根本原因与潜在问题
问题的根本原因在于JDBC驱动的不当实现。它应该监听应用程序卸载事件并自行注销。
当Java SPI加载JDBC驱动时,它使用当前上下文的类加载器加载。由于驱动位于应用的WEB-INF/lib
中,SPI使用其类加载器加载。这种方式加载的驱动会被注册到DriverManager
类(一个JVM单例)中。如果没有这样做,将会引入加载类的内存泄漏。
当我们卸载web应用时,其类加载器会被垃圾回收。然而,DriverManager
仍然引用JDBC驱动,阻止了垃圾回收。如果我们再次部署相同的web应用,会创建一个新的类加载器,SPI会再次加载相同的JDBC驱动,从而形成内存泄漏。
4. 缓解措施
有多种方法可以缓解这个问题。
4.1. 使用更新的Tomcat版本
自6.0.24版本起,Tomcat会自动为我们处理这个问题。这意味着我们可以安全地忽略警告消息。
4.2. 手动在退出回调时注销
我们可以在应用的任何退出回调中手动注销驱动。在标准情况下,如果我们的应用只加载了一个JDBC驱动,只需一行代码即可完成:
DriverManager.deregisterDriver(DriverManager.getDrivers().nextElement());
需要注意的是,虽然Tomcat称为“注销”,但实际调用的是DriverManager
的deregistration方法。
4.3. 移动JDBC JAR文件
官方处理方式是将JDBC驱动的JAR文件从应用的WEB-INF/lib
移动到Tomcat的lib
目录。因为lib
目录下的所有JAR文件也在类路径中,Tomcat仍然会自动加载驱动,但使用自己的类加载器加载。
当我们部署应用时,Tomcat不会加载任何驱动实现,因为WEB-INF/lib
下不会有。这意味着我们可以安全地卸载并重新部署,而不会加载新的内容,从而防止任何泄漏。
5. 总结
在这篇文章中,我们详细讨论了Tomcat关于JDBC驱动强制注销警告消息的含义,以及其根源和解决方法。