Quartz是一款定时任务调度的开源框架,使用起来比较方便。并且Spring的support包对Quartz有集成。但是笔者在web应用使用的过程中却遇到了内存泄漏的问题。
问题的产生
笔者在使用Spring+Quartz的用法如下(熟悉Spring+Quartz的可以跳过直接看问题):
1.配置Scheduler工厂
2.编写Job的实现类QuartzJob,用来执行任务
//这里须继承spring的QuartzJobBean
public class QuartzJob extends QuartzJobBean {/**
? ? * 任务执行内容。
? ? */
? ? @Override
? ? protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
? ? ? ? // 执行任务的代码
? ? }
}
3.将Scheduler工厂生产的scheduler注入到业务逻辑类中。这里注意,注入的其实是由schedulerFactory生产出来的scheduler实例(对Spring不熟悉的人会自然而然的认为注入的是schedulerFactory的实例,这是因为Spring的SchedulerFactoryBean实现了FactoryBean接口,所以注入的结果会是FactoryBean.getObject()获得的实例,题外话)。
? ?
? ? ?
? ?
?
public class TaskServiceImpl {
? ? private Scheduler scheduler;/**
? ? * 由spring通过工厂注入,此实例为单例。
? ? * @param scheduler
? ? */
? ? public void setScheduler(Scheduler scheduler) {
? ? ? ? this.scheduler = scheduler;
? ? }
4.在业务逻辑类TaskServiceImpl中,使用scheduler执行任务。这里可以动态对任务执行增加、删除、重启等等操作。还有一个重要的特性是可以动态的改变Cron表达式,对任务的定时规则进行更改。
1? ? /**
?2? ? ? * 新增任务。
?3? ? ? * @param jobName
?4? ? ? * @param jobGroup
?5? ? ? * @param cron
?6? ? ? */
?7? ? public void addJob(String jobName, String jobGroup, String cron, String kindId) {
?8? ? ? ? try {
?9? ? ? ? ? ? if (StringUtil.isEmpty(jobName) || StringUtil.isEmpty(jobGroup) || StringUtil.isEmpty(cron)) {
10? ? ? ? ? ? ? ? throw new BusinessException("定时任务创建失败,参数为空");
11? ? ? ? ? ? }
12? ? ? ? ? ? JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity(jobName, jobGroup).build();
13? ? ? ? ? ? jobDetail.getJobDataMap().put("JobKind", kindId);
14? ? ? ? ? ? //表达式调度构建器
15? ? ? ? ? ? CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
16? ? ? ? ? ? //按新的cronExpression表达式构建一个新的trigger
17? ? ? ? ? ? CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
18? ? ? ? ? ? scheduler.scheduleJob(jobDetail, trigger);
19? ? ? ? ? ? log.info("新增定时任务,name:" + jobName + ",group:" + jobGroup + ",cron:" + cron);
20? ? ? ? } catch (SchedulerException e) {
21? ? ? ? ? ? // 对异常的处理
22? ? ? ? }
23? ? }
注意18行的scheduler是由Spring注入的。
这种用法在使用过程中没有问题,但在web应用在Tomcat6中热部署时出险了以下严重警告,如果忽略这种警告,在频繁的reload后会造成Tomcat 的 Permgen space OOM。
Jun 24, 2014 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/feeder##1.5.0] appears to have started a thread named [scheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak.
Jun 24, 2014 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/feeder##1.5.0] appears to have started a thread named [scheduler_Worker-2] but has failed to stop it. This is very likely to create a memory leak.
Jun 24, 2014 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/feeder##1.5.0] appears to have started a thread named [scheduler_Worker-3] but has failed to stop it. This is very likely to create a memory leak.
Jun 24, 2014 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/feeder##1.5.0] appears to have started a thread named [scheduler_Worker-4] but has failed to stop it. This is very likely to create a memory leak.
Jun 24, 2014 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/feeder##1.5.0