【问题标题】:How to prevent a memory leak in quartz如何防止石英中的内存泄漏
【发布时间】:2012-05-29 02:02:20
【问题描述】:

我在我的项目中使用石英。我的 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.QuartzInitializerServlet 和org.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>

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

【问题讨论】:

    标签: java jsp tomcat servlets quartz-scheduler


    【解决方案1】:

    通过实现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)。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        如果您在 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>
        

        【讨论】:

        • 我有类似的实现和问题。 shutdown() 方法中的 true 对我有用
        【解决方案4】:

        我想你想要:

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

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

        【讨论】:

        猜你喜欢
        • 2017-01-03
        • 1970-01-01
        • 2010-12-20
        • 2011-08-09
        • 2012-03-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多