1. 程式人生 > >使用C3P0連線池的web專案在Tomcat8中redeploy後產生警告記憶體洩漏!

使用C3P0連線池的web專案在Tomcat8中redeploy後產生警告記憶體洩漏!

四月 23, 2018 10:50:29 下午 org.apache.catalina.startup.HostConfig reload
資訊: Reloading context [/bookcity]
四月 23, 2018 10:50:29 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/bookcity] has started
四月 23, 2018 10:50:29 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesJdbc
警告: The web application [bookcity] 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.
四月 23, 2018 10:50:29 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
警告: The web application [bookcity] appears to have started a thread named [Timer-2] 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.util.TimerThread.mainLoop(Timer.java:552)
 java.util.TimerThread.run(Timer.java:505)
四月 23, 2018 10:50:29 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
警告: The web application [bookcity] appears to have started a thread named [C3P0PooledConnectionPoolManager-Helper Thread-#0] 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)
 com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:607)
四月 23, 2018 10:50:29 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
警告: The web application [bookcity] appears to have started a thread named [C3P0PooledConnectionPoolManager-Helper Thread-#1] 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)
 com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:607)
四月 23, 2018 10:50:29 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
警告: The web application [bookcity] appears to have started a thread named [C3P0PooledConnectionPoolManager-Helper Thread-#2] 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)
 com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:607)
四月 23, 2018 10:50:31 下午 org.apache.jasper.servlet.TldScanner scanJars
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
四月 23, 2018 10:50:31 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/bookcity] is completed


以上是console輸出資訊!



    這幾個是重點!原因是使用了C3P0連線池時,建立資料來源後Tomcat不能幫我們釋放連線池中的所有連線和關閉連線池執行緒!即下面這段程式碼建立的C3P0資料來源!

private static final ComboPooledDataSource ds = new ComboPooledDataSource();

    解決辦法是註冊一個實現了ServletContextListener的監聽器,監聽伺服器關閉該web應用時呼叫ComboPooledDataSource類的close方法關閉連線池。具體實現如下:

    下面是建立C3P0資料來源的工具類:

package cn.cdut.util.jdbc;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;


/**
 * 用Jdbc操作資料庫的工具,本類依賴c3p0-config.xml檔案
 * @author huangyong
 * @date 2018年4月8日 下午9:58:31
 *
 */
public class JdbcUtils {
	private static final ComboPooledDataSource ds = new ComboPooledDataSource();

	/**
	 * 獲得資料來源
	 * @return DataSource   返回型別
	 */
	public static DataSource getDataSource(){
		return ds;
	}

	/**
	 *  關閉資料來源
	 * @return void   返回型別
	 */
	public static void closeDataSource(){
		ds.close();
	}
}
下面是監聽器:
package cn.cdut.bookcity.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import cn.cdut.util.jdbc.JdbcUtils;

/**
 * 關閉ComboPooledDataSource連線池監聽器
 * @author huangyong
 * @date 2018年4月23日 下午8:54:34
 */
public class CloseDataSourceListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent arg0) {

	}

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		JdbcUtils.closeDataSource();
	}
}

在web.xml中註冊監聽器

<listener>
  	<listener-class>cn.cdut.bookcity.listener.CloseDataSourceListener</listener-class>
</listener>

經過幾次redploy測試後以上情況沒有出現,但出現了新的警告,意思是JDBC驅動登出失敗被強制登出!

四月 23, 2018 11:14:44 下午 org.apache.catalina.startup.HostConfig reload
資訊: Reloading context [/bookcity]
四月 23, 2018 11:14:44 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/bookcity] has started
四月 23, 2018 11:14:44 下午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesJdbc
警告: The web application [bookcity] 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.
四月 23, 2018 11:14:45 下午 org.apache.jasper.servlet.TldScanner scanJars
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
四月 23, 2018 11:14:45 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/bookcity] is completed

又經過查閱後修改程式碼如下:

package cn.cdut.bookcity.listener;

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import cn.cdut.util.jdbc.JdbcUtils;

/**
 * 監聽器<br>
 * 關閉ComboPooledDataSource連線池,登出所有註冊驅動
 * @author huangyong
 * @date 2018年4月23日 下午8:54:34
 */
public class CloseDataSourceListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent arg0) {

	}

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		JdbcUtils.closeDataSource();
		//登出驅動
		//得到所有註冊了的驅動
		Enumeration<Driver> drivers = DriverManager.getDrivers();
		//遍歷登出
		while(drivers.hasMoreElements()){
			try {
				DriverManager.deregisterDriver(drivers.nextElement());
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
總結出還是框架大法好!