tomcat热部署内存泄漏问题 create a memory leak

目录

一、问题由来

二、产生原因

三、解决办法

方法1:新建一个context的监听器

方法2:在tomcat context.xml配置文件中,添加三个属性(未测试)

四、模拟问题场景--多次热部署同一应用

1、本地环境配置

2、打开jconsole进行监控

3、reload应用一次

4、反复重试第3步

5、查看tomcat下logs中的catalina.log


一、问题由来

我们组部署服务的方式,是将项目打成war包,然后拷贝到服务器tomcat的webapps目录下,虽然不需要重启tomcat,但是在log目录下catalina.out日志中会看到类似于下面的WARNING报错:

org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [DemoProject] appears to have started a thread named [com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0] but has failed to stop it. This is very likely to create a memory leak.

大概意思为线程池的线程资源无法释放,容易造成内存溢出。

 

二、产生原因

一般情况下,如果我们重启web应用是通过重启tomcat的话,则不存在内存泄漏问题。

是什么原因导致tomcat内存泄漏的。这个要从热部署开始说起,因为tomcat提供了不必重启容器而只需重启web应用以达到热部署的功能,其实现是通过定义一个WebappClassLoader类加载器,当热部署时就将原来的类加载器废弃并重新实例化一个WebappClassLoader类加载器。但这种方式可能存在内存泄漏问题,因为ClassLoader是一个结构复杂的对象,导致它不能被GC回收的可能性比较多,除了对ClassLoader对象有引用可能导致其无法回收,还可能对其加载的元数据(方法、类、字段等)有引用都会导致无法被GC回收。

参考:https://www.jb51.net/article/101745.htm

在实际项目中,主要是这几类资源无法释放:

1,连接池

2,自己写的任务队列线程,

3,mysql的线程;

 

三、解决办法

方法1:新建一个context的监听器

1)如果是连接池,可以新一个context的监听器

自定义实现一个javax.servlet.ServletContextListener,重写contextDestroyed方法,然后注册到servlet中。

public class ContextDestroyedListener implements ServletContextListener {
 
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("you must be reload tomcat....please waiting ...");
          CacheManager.shutdown();
          Enumeration<Driver> drivers = DriverManager.getDrivers();
             while (drivers.hasMoreElements()) {
                 Driver driver = drivers.nextElement();
                 try {
                     DriverManager.deregisterDriver(driver);
                     System.out.println("stoping....  "+driver.toString());
                 } catch (SQLException e) {
                     e.printStackTrace();
                 }
             }
             Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
             Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
             for(Thread t:threadArray) {
                 if(t.getName().contains("Abandoned connection cleanup thread")||t.getName().contains("task_excutor")) {
                     synchronized(t) {
                         t.stop();
                     }
                 }
             }
    }
 
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
 
    }
 
}

在web.xml里面配置监听。

2)如果是自己的线程可以使用线程名称来关闭

if(t.getName().contains("Abandoned connection cleanup thread")||t.getName().contains("task_excutor")) {
    synchronized(t) {
        t.stop();
    }
 }

方法2:在tomcat context.xml配置文件中,添加三个属性(未测试)

maxWait="2000"  //单位毫秒
removeAbandoned="true"  
removeAbandonedTimeout="180"   //单位秒
即增加连接池回收机制,活动连接,在超过时间内,没有使用,会被自动回收。

个人感觉这个没啥用,主要之前没用过。

<Context path="/kaka" docBase="kaka" debug="0" reloadbale="true" privileged="true">  
  
<WatchedResource>WEB-INF/web.xml</WatchedResource>  
  
<Resource name="jdbc/testSiteds"
    auth="Container"
    type="javax.sql.DataSource"  
    maxActive="100"
    maxIdle="30"
    maxWait="10000"
    username="root"
    password="root"
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/testSite"
    maxWait="2000"
    removeAbandoned="true"  
    removeAbandonedTimeout="180"/>
</Context>

四、模拟问题场景--多次热部署同一应用

参考:https://www.cnblogs.com/grey-wolf/p/10179895.html

1、本地环境配置

一个自己的war包,两个tomcat自带的war包,用来控制reload应用,配置好后,启动tomcat。

2、打开jconsole进行监控

点击JDK/bin 目录下面的jconsole.exe 即可启动,主要是监控线程。

3、reload应用一次

打开localhost:9080/manager/html,如果不能访问,请在tomcat下面的conf中的tomcat-users.xml配置:

    <role rolename="manager-gui"/>
    <user username="admin" password="admin" roles="manager-gui"/>

观察jconsole中的线程数是否增加

4、反复重试第3步

如果不出意外(程序中有线程泄漏)的话,jconsole中的线程图应该是下面这样,一步一个台阶:

5、查看tomcat下logs中的catalina.log

这里面可能会有些线程泄漏的警告,如下:

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值