你好,游客 登录 注册 搜索
背景:
阅读新闻

Spring3.2.11与Quartz2.2.1整合时内存泄漏问题解决

[日期:2015-06-18] 来源:Linux社区  作者:Linux [字体: ]

Quartz是一款定时任务调度的开源框架,使用起来比较方便。并且Spring的support包对Quartz有集成。但是笔者在web应用使用的过程中却遇到了内存泄漏的问题。

问题的产生

笔者在使用Spring+Quartz的用法如下(熟悉Spring+Quartz的可以跳过直接看问题):

1.配置Scheduler工厂

<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
</bean>

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()获得的实例,题外话)。

<bean id="taskService" class="com.xxxx.xx.service.impl.TaskServiceImpl" init-method="init" scope="singleton">
    <property name="scheduler">
      <ref bean="schedulerFactory"/>
    </property>
  </bean>

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] appears to have started a thread named [scheduler_Worker-5] 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-6] 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

问题的原因

究其原因是开启的scheduler_Worker线程没有关闭。但Spring的SchedulerFactoryBean实现了DisposableBean接口,表示web容器关闭时会执行destroy(),执行内容如下:

  /**
    * Shut down the Quartz scheduler on bean factory shutdown,
    * stopping all scheduled jobs.
    */
    public void destroy() throws SchedulerException {
        logger.info("Shutting down Quartz Scheduler");
        this.scheduler.shutdown(this.waitForJobsToCompleteOnShutdown);
    }

表示工厂已经做了shutdown。所以,问题出现在真正执行的scheduler.shutdown(true)。

原来这是Quartz的bug(见https://jira.terracotta.org/jira/browse/QTZ-192),在调用scheduler.shutdown(true)后,Quartz检查线程并没有等待那些worker线程被停止就结束了。

网上说在Quartz2.1版本已经修补了这个bug,但笔者使用的2.2.1版本仍旧有问题。

问题的解决

那么问题如何解决,这里有一个不太美观的方案:在调用scheduler.shutdown(true)后,增加一点睡眠时间,等待worker线程全部停止。如下:

1。自定义schedulerFactory继承Spring的SchedulerFactoryBean,覆盖destroy()增加延时。

public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean {

  /**
    * 关于Quartz内存泄漏的不太美观的解决方案:
    * 在调用scheduler.shutdown(true)后增加延时,等待worker线程结束。
    */
    @Override
    public void destroy() throws SchedulerException {
        super.destroy();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

2。把spring配置文件中的SchedulerFactoryBean替换为自定义的工厂即可。

<bean id="schedulerFactory" class="com.sinosoft.ms.task.SchedulerFactoryBeanWithShutdownDelay">
</bean>

OK,问题解决。

Spring配置Quartz任务调度框架教程  http://www.linuxidc.com/Linux/2014-11/108907.htm

Quartz深入浅出 http://www.linuxidc.com/Linux/2014-09/107007.htm

Quartz1.6有状态JOB碰到的棘手问题既解决方案 http://www.linuxidc.com/Linux/2014-09/107005.htm

Spring 3整合Quartz 2实现定时任务 http://www.linuxidc.com/Linux/2014-09/107006.htm

Java项目中定时任务之Quartz的应用 http://www.linuxidc.com/Linux/2013-12/94443.htm

Spring 3 调度器示例 —— JDK 定时器和 Quartz 展示 http://www.linuxidc.com/Linux/2013-10/91946.htm

本文永久更新链接地址http://www.linuxidc.com/Linux/2015-06/119042.htm

linux
相关资讯       Quartz  Spring3.2.11  Quartz2.2.1 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款