您的位置:首页 > 运维架构 > Tomcat

"Tomcat+Spring+Quartz"解决方案下,关闭Tomcat出现"线程未关闭,出现内存泄漏"错误

2015-08-04 15:06 951 查看
使用"Tomcat+Spring+Quartz"解决方案,在关闭Tomcat时出现如图1所示错误信息:

图1



这里使用的Tomcat版本为6.2.32,Spring版本为3.2.3,Quartz版本为1.8.6

一、原因分析

在"Tomcat+Spring+Quartz"的解决方案中,Tomcat在运行的时候,Spring中配置的Quartz SchedulerFactoryBean会创建多个工作线程。在Tomcat关闭的时候,检测到这些线程没有得到及时的销毁,因而出现如图1的错误。

二、解决方案

Tomcat关闭的时候,会去销毁Spring实例。根据Spring的生命周期机制,在销毁Spring实例的过程中,会去处理Spring中配置的Bean实例的销毁事宜。只要Spring中配置的Bean实例符合以下图2中的任意一个条件

图2



Spring实例销毁过程中就会去调用这些Bean实例上的设置的销毁方法。

在我们的项目中,在Spring中配置Quartz SchedulerFactoryBean的片段如下:

<bean id="startQuartz" lazy-init="false"  autowire="no"  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="doTime"/>
</list>
</property>
</bean>
查看org.springframework.scheduling.quartz.SchedulerFactoryBean的源代码,发现它继承org.springframework.beans.factory.DisposableBean接口,满足图2中的条件。因而在Spring实例的销毁过程中,SchedulerFactoryBean实例的destroy()方法会被自动调用。

因而网上很多“使调用SchedulerFactoryBean实例的销毁方法”的解决方案都是错误的,这些解决方案包括“在Spring中定义SchedulerFactoryBean实例时增加‘destroy-method’属性”,“实现一个监听器,使得在Tomcat销毁时去调用SchedulerFactoryBean实例的销毁方法”等。

实际上,产生以上错误的原因是在销毁过程中,SchedulerFactoryBean实例的destroy()方法的运行会给它创建的线程发送关闭指令,然后destroy()方法就运行完毕返回。但是其实这些线程真正关闭还是需要一定时间的,这产生了时延,因此当destroy()方法运行完毕返回,然后相应的Spring实例的销毁过程完成之后,Tomcat认为理应没有线程在运行,但是却发现还有线程在运行,因此就会抛出以上错误。

那么一种解决方案就是让SchedulerFactoryBean实例的destroy()方法等待一段时间再完成返回,相应的Spring实例的销毁过程也会等待该段时间,在这段时间内这些Quartz SchedulerFactoryBean的线程得以关闭,接着Tomcat检查的时候就不会抛出还有线程在运行出现内存泄漏的错误。

我创建了如下类:

package com.dslztx.utils;

import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean {

@Override
public void destroy() throws SchedulerException {
super.destroy();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
相应在Spring中的配置片段如下:

<bean id="startQuartz" lazy-init="false" autowire="no"  class="com.dslztx.utils.SchedulerFactoryBeanWithShutdownDelay">
<property name="triggers">
<list>
<ref bean="doTime"/>
</list>
</property>
</bean>


三、其他

1、由于这些Quartz SchedulerFactoryBean创建的线程最后能够得以关闭,因此Tomcat进程也能被正常关闭,在有些情形中,Tomcat进程内的线程不能得以关闭,那么Tomcat进程也不能被正常关闭,只能通过kill命令,强行杀死进程。

2、这个问题已经在Quartz 2.1上修复

参考文献:

[1]http://forums.terracotta.org/forums/posts/list/3479.page

[2]http://stackoverflow.com/questions/2730354/spring-scheduler-shutdown-error

[3]https://jira.terracotta.org/jira/browse/QTZ-192

[4]http://forum.spring.io/forum/spring-projects/container/25869-quartz-doesn-t-shutdown
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: