如何防止石英中的内存泄漏

时间:2012-05-22 14:47:22

标签: java jsp tomcat servlets quartz-scheduler

我在项目中使用石英。我的Web应用程序显然在停止时导致内存泄漏,错误是:

SEVERE: A web application appears to have started a TimerThread named [Timer-12] via the java.util.Timer API but has failed to stop it. To prevent a memory leak, the timer (and hence the associated thread) has been forcibly cancelled. 
Jan 2, 2013 6:55:35 AM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: A web application appears to have started a thread named [DefaultQuartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak.

我使用了org.quartz.ee.servlet.QuartzInitializerServletorg.quartz.ee.servlet.QuartzInitializerListener。我工厂的代码是:

StdSchedulerFactory factory = (StdSchedulerFactory) context.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY );

和web.xml中的quartz设置为:

<servlet>
         <servlet-name>
             QuartzInitializer
         </servlet-name>
         <display-name>
             Quartz Initializer Servlet
         </display-name>
         <servlet-class>
             org.quartz.ee.servlet.QuartzInitializerServlet
         </servlet-class>
         <load-on-startup>
             1
         </load-on-startup>
         <init-param>
             <param-name>shutdown-on-unload</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>wait-on-shutdown</param-name>
             <param-value>true</param-value>
         </init-param>
         <init-param>
             <param-name>start-scheduler-on-load</param-name>
             <param-value>true</param-value>
         </init-param>
     </servlet>
     <context-param>
         <param-name>quartz:shutdown-on-unload</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:wait-on-shutdown</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:start-on-load</param-name>
         <param-value>true</param-value>
     </context-param>
     <listener>
         <listener-class>
             org.quartz.ee.servlet.QuartzInitializerListener
         </listener-class>
     </listener>

请帮我解决这个内存泄漏!!

4 个答案:

答案 0 :(得分:4)

通过实现org.quartz.InterruptableJob,您可以正确地中断由servlet卸载触发的线程。

@DisallowConcurrentExecution
public class Job implements InterruptableJob {

    private Thread thread;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        thread = Thread.currentThread();
        // ... do work
    }

    @Override
    public void interrupt() throws UnableToInterruptJobException {
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new UnableToInterruptJobException(e);
        } finally {
            // ... do cleanup
        }
    }
}

如果作业在中断之前尚未执行,则此示例可能会导致线程变量出现争用条件错误。我将最终解决方案打开以获取建议,具体取决于目标应用程序的生命周期。如果需要通过同一个作业实例进行并发执行,请扩充解决方案以处理多个线程并删除@DisallowConcurrentExecution注释。

为了使其工作,石英属性org.quartz.scheduler.interruptJobsOnShutdownWithWait必须设置为true。这可以通过为调度程序定义属性文件来完成,或者如果使用spring框架则通过bean引用来完成。

示例quartz.properties文件:

org.quartz.scheduler.interruptJobsOnShutdownWithWait=true

注意如果调度程序配置为在关机时等待,则仅调度中断,从而调用scheduler.shutdown(true)

答案 1 :(得分:2)

我看到你初始化了两个实例...... - 首先通过org.quartz.ee.servlet.QuartzInitializerServlet - 第二个通过org.quartz.ee.servlet.QuartzInitializerListener

删除QuartzInitializerServlet或QuartzInitializerListener(以及相应的参数)...... 如果你想拥有多个实例(出于特定原因),请使用QuartzInitializerServlet(并且不要忘记每个实例使用不同的实例)

答案 2 :(得分:1)

如果您正在为Web应用程序使用自己的ServletContextListener接口实现,则可以在contextDestroyed方法中正常关闭Quartz。请在下面找到Quartz版本2.1.7的示例代码。

你的工作:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class CronJob implements Job {
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        // TODO: do you job
    }
}

您的工作安排员:

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class CronJobScheduler {

    private static CronJobScheduler instance = new CronJobScheduler();  
    private Scheduler scheduler;

    private CronJobScheduler() {    
        try {
            scheduler = new StdSchedulerFactory().getScheduler();
        } catch (SchedulerException e) {
            // TODO
        }
    }

    public static CronJobScheduler getInstance() {
        return instance;
    }

    public void trigger() {
        JobKey jobKey = JobKey.jobKey("myJobName", "myJobGroup");       
        JobDetail job = JobBuilder.newJob(CronJob.class).withIdentity(jobKey).build();

        Trigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity("myTriggerName", "myJobGroup")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 1,13 * * ?"))
                .build();

        try {
            scheduler.start();
            scheduler.scheduleJob(job, trigger);
        } catch (SchedulerException e) {    
            // TODO
        }
    }

    public void shutdown(boolean waitForJobsToComplete) {
        try {
            scheduler.shutdown(waitForJobsToComplete);
        } catch (SchedulerException e) {
            // TODO
        }
    }

}

您的ServletContextListener接口的实现:

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        CronJobScheduler.getInstance().shutdown(true);
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        CronJobScheduler.getInstance().trigger();
    }

}

您的web.xml

<listener>
    <listener-class>my.package.name.MyServletContextListener</listener-class>
</listener>

答案 3 :(得分:0)

我想你想要:

     <init-param>
         <param-name>wait-on-shutdown</param-name>
         <param-value>true</param-value>
     </init-param>

你有一个“quartz:”前缀,可能导致Quartz恢复为该配置设置的默认值“false”。