一. 重新部署,发生异常,Tomcat停止
背景条件:
-
使用tomcat版本:apache-tomcat-8.5.91
-
使用连接池版本:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.15</version> </dependency>
问题:
-
在IDEA进行tomcat部署,然后访问web数据,在进行重新部署抛异常:
[mysql-cj-abandoned-connection-cleanup] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading 非法访问:此Web应用程序实例已停止。无法加载[]。为了调试以及终止导致非法访问的线程,将抛出以下堆栈跟踪。
-
解决办法:①在项目中新建一个名为
ContextFinalizer
的监听类,具体代码如下:package com.panziye.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; @WebListener public class ContextFinalizer implements ServletContextListener{ public void contextInitialized(ServletContextEvent sce) {} public void contextDestroyed(ServletContextEvent sce) { Enumeration<Driver> drivers = DriverManager.getDrivers(); Driver d = null; while (drivers.hasMoreElements()) { try { d = drivers.nextElement(); DriverManager.deregisterDriver(d); } catch (SQLException ex) { } } try { // 注意:mysql8版本的jar好像shutdown方法私有了,只能调用checkedShutdown或uncheckedShutdown AbandonedConnectionCleanupThread.checkedShutdown(); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
@WebListener
这个注解相当于在web.xml
配置如下内容:<listener> <listener-class> com.panziye.listener.ContextFinalizer </listener-class> </listener>
-
成功解决该异常
二. 内存泄露
承接上面
在IDEA中tomcat重新部署,报内存泄露危险:
Web应用程序[pro05]似乎启动了一个名为[Druid-ConnectionPool-Destroy-257280247]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:
原因分析:
- 在进行tomcat重新部署的情况下,
dataSource
未能及时关闭释放资源。 - 在进行主程序停止时,未能及时停止它,造成系统资源浪费。
解决过程:
-
解决思路:进行连接池的关闭操作。
-
发现
dataSource
对象中并没有,close()方法。 -
查看 阿里 Druid 源代码,发现其
DruidDataSource
是有 close() 方法的。-
在
DruidDataSourceFactory.java
文件中,有以下源码:@SuppressWarnings("rawtypes") public static DataSource createDataSource(Map properties) throws Exception { // 进行了向上转型 DruidDataSource dataSource = new DruidDataSource(); config(dataSource, properties); return dataSource; }
-
而
DruidDataSource.java
的源代码:
-
得出dataSource的运行时对象是可以进行 close() 方法。
-
-
在监听器中添加以下源码:
package com.panziye.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; @WebListener public class ContextFinalizer implements ServletContextListener{ public void contextInitialized(ServletContextEvent sce) {} public void contextDestroyed(ServletContextEvent sce) { Enumeration<Driver> drivers = DriverManager.getDrivers(); Driver d = null; while (drivers.hasMoreElements()) { try { d = drivers.nextElement(); DriverManager.deregisterDriver(d); } catch (SQLException ex) { } } try { System.out.println("===============进行连接池关闭释放=========================="); /* JDBCUtils中的close()方法,其中 dataSource 是 JDBCUtils的静态属性 public static void closeDruidDataSource(){ DruidDataSource druidDataSource = (DruidDataSource) dataSource; druidDataSource.close(); } */ JDBCUtils.closeDruidDataSource(); // 注意:mysql8版本的jar好像shutdown方法私有了,只能调用checkedShutdown或uncheckedShutdown AbandonedConnectionCleanupThread.checkedShutdown(); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
进行重新部署,问题完美解决: