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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10704418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 02:11:11  来源:igfitidea点击:

How to prevent a memory leak in quartz

javajsptomcatservletsquartz-scheduler

提问by Ali Farozi

I'm using quartz in my project. My web application has apparently caused a memory leak when it stops, the error is :

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

I used org.quartz.ee.servlet.QuartzInitializerServletand org.quartz.ee.servlet.QuartzInitializerListener. The code for my factory is:

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

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

and settings for quartz in web.xml is :

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>

please help me to solve this memory leak !!

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

回答by Martin

By implementing org.quartz.InterruptableJobyou can properly interrupt threads triggered by servlet unloading.

通过实现,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
        }
    }
}

This example may cause a race condition bug on the thread variable, if the job has not been executed before it is interrupted. I leave the final solution open for suggestions, depending on the life cycle of the target application. If you need concurrent execution through the same job instance, augment the solution to handle multiple threads and remove the @DisallowConcurrentExecutionannotation.

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

In order for this to work the quartz property org.quartz.scheduler.interruptJobsOnShutdownWithWaitmust be set to true. This can be done by defining a property file for the scheduler, or by a bean references if using spring framework.

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

Example quartz.propertiesfile:

示例quartz.properties文件:

org.quartz.scheduler.interruptJobsOnShutdownWithWait=true

Notethat the interruption only is dispatched if the scheduler is configured to wait on shutdown, resulting in a call to scheduler.shutdown(true).

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

回答by MarMax

I see you initialize two instances... - first through org.quartz.ee.servlet.QuartzInitializerServlet - second through org.quartz.ee.servlet.QuartzInitializerListener

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

Either remove QuartzInitializerServlet or QuartzInitializerListener (and also corresponding parameters)... If you want to have multiple instances (for specific reasons), go with the QuartzInitializerServlet (and do not forget to use different per instance)

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

回答by Yuci

If you are using your own implementation of the ServletContextListener interface for your web application, you can shutdown Quartz gracefully in the contextDestroyed method. Please find below the sample code for Quartz version 2.1.7.

如果你正在为你的 web 应用程序使用你自己的 ServletContextListener 接口实现,你可以在 contextDestroyed 方法中优雅地关闭 Quartz。请在下方找到 Quartz 2.1.7 版的示例代码。

Your job:

你的工作:

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
    }
}

Your job scheduler:

您的作业调度程序:

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
        }
    }

}

Your implementation of the ServletContextListener interface:

您对 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();
    }

}

Your web.xml

你的 web.xml

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

回答by Christopher Schultz

I think you want:

我想你想要:

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

You have a "quartz:" prefix which may be causing Quartz to revert to the default value of "false" for that configuration setting.

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