歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Spring3.2.11與Quartz2.2.1整合時內存洩漏問題解決

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

Copyright © Linux教程網 All Rights Reserved