Tomcat 中的 Quartz 调度程序内存泄漏
Quartz Scheduler Memory Leak in Tomcat
我使用石英版2.2.2
和Spring启动版1.3.1
。它的行为正常,并且一切正常。但是当我试图关闭这个应用程序时,问题就出现了。日志显示存在内存泄漏...
我的 Quartz 配置;
org.quartz.scheduler.instanceName = my-app-jobs
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.threadCount=2
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.scheduler.skipUpdateCheck = true
我有 2 个 Classes 执行如下工作;
/**
*
* To Run Every 15 Minutes
*
*/
public class MemoryDataUpdateJob implements Job {
@Autowired
private Dao dao;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Logger.info(getClass().getName(), "Scheduled Memory Data Update Job Started...");
//Read Device Settings
dao.readDeviceSettings();
Logger.info(getClass().getName(), "Scheduled Memory Data Update Job Finished.");
}
}
其他Class也是类似的做不同的事情。
Scheduler Factory Bean如下;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
我收到如下 tomcat 日志;
19-Jan-2016 11:18:39.722 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp-1.0.0] appears to have started a thread named [schedulerFactoryBean_Worker-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.lang.Object.wait(Native Method)
19-Jan-2016 11:18:39.722 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp-1.0.0] appears to have started a thread named [schedulerFactoryBean_Worker-2] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.lang.Object.wait(Native Method)
浏览了 quartz 文档并在属性中添加了以下内容;
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownhook.cleanShutdown = true
第一个线程消息消失了,但关于 worker-2
内存泄漏的第二个消息仍然存在。
但是在我的应用程序记录器中,我看到了这样的东西;
[2016-01-19 11:18:39:681] [DEBUG] [schedulerFactoryBean_Worker-1] [org.quartz.simpl.SimpleThreadPool:612] - WorkerThread is shut down.
[2016-01-19 11:18:40:047] [DEBUG] [schedulerFactoryBean_Worker-2] [org.quartz.simpl.SimpleThreadPool:612] - WorkerThread is shut down.
[2016-01-19 11:18:40:049] [INFO ] [Quartz Shutdown-Hook schedulerFactoryBean] [org.quartz.plugins.management.ShutdownHookPlugin:126] - Shutting down Quartz...
这里有什么问题?为什么即使应用程序记录器显示线程已关闭,也会出现内存泄漏?
知道了。问题是 tomcat 正在关闭,但石英作业是 运行。如果我们等待 1 秒之类的时间,它就会正常关闭。我们可以通过扩展 SchedulerFactoryBean
并覆盖它的 destroy
方法来实现。如下所示;
public class SchedulerFactoryBeanWithWait extends SchedulerFactoryBean {
@Override
public void destroy() throws SchedulerException {
super.destroy();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logError(e);
}
}
}
和 bean 声明;
@Bean
public SchedulerFactoryBeanWithWait schedulerFactoryBeanWithWait (
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBeanWithWait factory = new SchedulerFactoryBeanWithWait ();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
这让它一直等到线程关闭。也许将来会对某人有所帮助。
至少从 Spring 3.0 开始,SchedulerFactoryBean 就有一个标志告诉它等待作业完成。设置标志并确保在关闭时调用 destroy 方法:
@Bean(destroyMethodName="destroy")
public SchedulerFactoryBean schedulerFactoryBean(
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setWaitForJobsToCompleteOnShutdown(true);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
或使用 XML 配置:
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
destroy-method="destroy">
<property name="waitForJobsToCompleteOnShutdown" value="true" />
....
</bean>
我使用石英版2.2.2
和Spring启动版1.3.1
。它的行为正常,并且一切正常。但是当我试图关闭这个应用程序时,问题就出现了。日志显示存在内存泄漏...
我的 Quartz 配置;
org.quartz.scheduler.instanceName = my-app-jobs
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.threadCount=2
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.scheduler.skipUpdateCheck = true
我有 2 个 Classes 执行如下工作;
/**
*
* To Run Every 15 Minutes
*
*/
public class MemoryDataUpdateJob implements Job {
@Autowired
private Dao dao;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Logger.info(getClass().getName(), "Scheduled Memory Data Update Job Started...");
//Read Device Settings
dao.readDeviceSettings();
Logger.info(getClass().getName(), "Scheduled Memory Data Update Job Finished.");
}
}
其他Class也是类似的做不同的事情。
Scheduler Factory Bean如下;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
我收到如下 tomcat 日志;
19-Jan-2016 11:18:39.722 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp-1.0.0] appears to have started a thread named [schedulerFactoryBean_Worker-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.lang.Object.wait(Native Method)
19-Jan-2016 11:18:39.722 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp-1.0.0] appears to have started a thread named [schedulerFactoryBean_Worker-2] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.lang.Object.wait(Native Method)
浏览了 quartz 文档并在属性中添加了以下内容;
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownhook.cleanShutdown = true
第一个线程消息消失了,但关于 worker-2
内存泄漏的第二个消息仍然存在。
但是在我的应用程序记录器中,我看到了这样的东西;
[2016-01-19 11:18:39:681] [DEBUG] [schedulerFactoryBean_Worker-1] [org.quartz.simpl.SimpleThreadPool:612] - WorkerThread is shut down.
[2016-01-19 11:18:40:047] [DEBUG] [schedulerFactoryBean_Worker-2] [org.quartz.simpl.SimpleThreadPool:612] - WorkerThread is shut down.
[2016-01-19 11:18:40:049] [INFO ] [Quartz Shutdown-Hook schedulerFactoryBean] [org.quartz.plugins.management.ShutdownHookPlugin:126] - Shutting down Quartz...
这里有什么问题?为什么即使应用程序记录器显示线程已关闭,也会出现内存泄漏?
知道了。问题是 tomcat 正在关闭,但石英作业是 运行。如果我们等待 1 秒之类的时间,它就会正常关闭。我们可以通过扩展 SchedulerFactoryBean
并覆盖它的 destroy
方法来实现。如下所示;
public class SchedulerFactoryBeanWithWait extends SchedulerFactoryBean {
@Override
public void destroy() throws SchedulerException {
super.destroy();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logError(e);
}
}
}
和 bean 声明;
@Bean
public SchedulerFactoryBeanWithWait schedulerFactoryBeanWithWait (
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBeanWithWait factory = new SchedulerFactoryBeanWithWait ();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
这让它一直等到线程关闭。也许将来会对某人有所帮助。
至少从 Spring 3.0 开始,SchedulerFactoryBean 就有一个标志告诉它等待作业完成。设置标志并确保在关闭时调用 destroy 方法:
@Bean(destroyMethodName="destroy")
public SchedulerFactoryBean schedulerFactoryBean(
JobFactory jobFactory,
@Qualifier("MemoryDataUpdateJobTrigger") Trigger memoryDataUpdateJobTrigger,
@Qualifier("MsgCountJobTrigger") Trigger msgCountJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setWaitForJobsToCompleteOnShutdown(true);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(memoryDataUpdateJobTrigger, msgCountJobTrigger);
return factory;
}
或使用 XML 配置:
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
destroy-method="destroy">
<property name="waitForJobsToCompleteOnShutdown" value="true" />
....
</bean>