12

我在我的项目中使用石英。我的 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 中石英的设置是:

<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

4 回答 4

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注释。

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

示例quartz.properties文件:

org.quartz.scheduler.interruptJobsOnShutdownWithWait=true

请注意,仅当调度程序配置为等待关闭时才会调度中断,从而导致调用scheduler.shutdown(true).

于 2013-03-07T11:25:22.310 回答
2

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

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

于 2012-09-24T11:48:03.807 回答
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>
于 2016-05-06T11:01:49.503 回答
0

我想你想要:

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

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

于 2012-05-22T15:50:10.557 回答