【解决】com.mysql.cj.jdbc.Driver failed to unregister it /Abandoned connection cleanup thread
错误:
16-May-2018 19:34:22.638 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [ziyue] registered the JDBC driver ** [com.mysql.cj.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.
16-May-2018 19:34:22.639 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [ziyue] 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. Stack trace of thread:
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:70)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)
在搭建的SSM框架(JDK1.8,使用DBCP和Druid连接池)中,在关闭Tomcat9.0的时候报出如下错误,虽然只是两个警告,但是总是非常不爽的。
通过这个报错就可以看出在关闭Tomcat的时候,有两个资源没有释放导致的警告。所以如下通过释放这两个资源的解决办法。两个资源通过包名就可以知道是与数据库连接有关的资源,百度了一番,最终找到了解决方法。
解决:
解法1.添加一个XBasicDataSource类继承自BasicDataSource,并重写其close()方法
重点:在消除BUG的过程中,才发现这是DBCP连接池的一个BUG,我这里也是使用的官网给出的修复----官网错误报告。在以后的编程中,我使用阿里的Druid连接池也出现了同样的数据库连接未注销的错误,让我怀疑这究竟是不是连接池的BUG吶?无奈找寻DruidDateSource的关闭数据库连接的方法,没有找到,所以Druid连接池我使用第二种方法解决了。
import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
import org.apache.commons.dbcp2.BasicDataSource;
import java.sql.DriverManager;
import java.sql.SQLException;
public class XBasicDataSource extends BasicDataSource {
@Override
public synchronized void close() throws SQLException {
//以下两句代码分别对应两个资源的关闭
DriverManager.deregisterDriver(DriverManager.getDriver(getUrl()));
AbandonedConnectionCleanupThread.checkedShutdown();
super.close();
}
}
BasicDataSource是配置DBCP连接池的类,为了使用我们刚才的close()方法,此处的class需要使用我们刚写的XBasicDataSource 类
<!--此处是未重写之前的配置类
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">-->
<bean id="dataSource" class="com.ziyue.listener.XBasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}"/>
<!-- 连接池最大数量 -->
<property name="maxTotal" value="${maxActive}"/>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}"/>
<!-- 获取连接最大等待时间 -->
<property name="maxWaitMillis" value="${maxWait}"/>
</bean>
解法2.由于以上问题出现在Tomcat关闭的时候,也就是Web应用结束的时候,所以我们可以利用Web的监听器在Web应用关闭的时候注销这两个资源,同样,实现的监听器MyServletContextListener 实现ServletContextListener接口并重写contextDestroyed()方法
package com.ziyue.listener;
import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
//修复DBCP的BUG,https://issues.apache.org/jira/browse/DBCP-332
public class MyServletContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
//这里如果Web应用拥有多个数据库的连接,可以一并关闭
Enumeration<Driver> drivers = DriverManager.getDrivers();
Driver driver = null;
while (drivers.hasMoreElements()) {
try {
driver = drivers.nextElement();
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
}
}
AbandonedConnectionCleanupThread.checkedShutdown();
}
}
完成了监听器的编写,还需要在web.xml中配置为监听器才会在项目启动和关闭时候分别调用它的两个方法
<!--ServletContext监听器-->
<listener>
<listener-class>com.ziyue.listener.MyServletContextListener</listener-class>
</listener>
虽然上面两种方法都可以实现资源的关闭,不过既然属于数据库的相关资源,还是建议在数据库相关的类中去释放它们,这也符合各个组件各司其职的规矩,毕竟这也是DBCP官方给出的修复方案。