【解決】com.mysql.cj.jdbc.Driver failed to unregister it /Abandoned connection cleanup thread

轉自:【解決】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官方給出的修復方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章