Quartz is an open-source framework for scheduled task scheduling, which is convenient to use. and spring's support package is integrated with quartz. However, I encountered the problem of memory leak in the process of using the Web application.
The problem arises
The author uses Spring+quartz as follows (familiar with Spring+quartz can skip the direct look at the problem):
1. Configuring the Scheduler Factory
<id= "Schedulerfactory" class= " Org.springframework.scheduling.quartz.SchedulerFactoryBean "></Bean >
2. Write the implementation class Quartzjob for the job to perform the task
// This is where spring's Quartzjobbean is to be inherited. Span style= "color: #0000ff;" >public class quartzjob extends Span style= "color: #000000;" > Quartzjobbean { /** * task execution content. */ @Override protected void executeinternal (jobexecutioncontext context) throws Jobexecutionexception { //
3. Inject scheduler from the Scheduler factory into the business logic class. Note here that the injected is actually a scheduler instance produced by Schedulerfactory ( people who are unfamiliar with spring will naturally think that injecting is an instance of Schedulerfactory, This is because spring's Schedulerfactorybean implements the Factorybean interface, so the result of the injection is factorybean.getobject () An example of the obtained, off-topic).
<BeanID= "Taskservice"class= "Com.xxxx.xx.service.impl.TaskServiceImpl"Init-method= "Init"Scope= "Singleton"> < Propertyname= "Scheduler"> <refBean= "Schedulerfactory"/> </ Property> </Bean>
Public class Taskserviceimpl { private Scheduler Scheduler; /** * Injected by spring through the factory, this instance is a singleton. @param Scheduler */ Public void Setscheduler (Scheduler Scheduler) { this. Scheduler = Scheduler; }
4. In the Business logic class Taskserviceimpl, use Scheduler to perform the task. This allows you to dynamically add, delete, restart, and so on tasks. Another important feature is the ability to dynamically change cron expressions to change the timing rules of a task.
1 /**2 * Added Tasks. 3 * @paramJobName4 * @paramJobgroup5 * @paramCron6 */7 Public voidaddjob (String jobName, String jobgroup, String cron, String kindid) {8 Try {9 if(Stringutil.isempty (jobName) | | Stringutil.isempty (jobgroup) | |Stringutil.isempty (cron)) {Ten Throw NewBusinessexception ("Timed task creation failed with null parameter"); One } AJobdetail Jobdetail = Jobbuilder.newjob (quartzjob.class). Withidentity (JobName, Jobgroup). Build (); -Jobdetail.getjobdatamap (). Put ("Jobkind", Kindid); - //Expression Scheduler Builder theCronschedulebuilder Schedulebuilder =Cronschedulebuilder.cronschedule (cron); - //build a new trigger according to the new cronexpression expression -Crontrigger trigger =Triggerbuilder.newtrigger (). Withidentity (JobName, Jobgroup). Withschedule (Schedulebuilder). build (); - scheduler.schedulejob (Jobdetail, trigger); +Log.info ("New timed task, Name:" + JobName + ", Group:" + Jobgroup + ", cron:" +cron); -}Catch(schedulerexception e) { + //Handling of exceptions A } at}
Note that the scheduler of line 18 is injected by spring.
This usage is not problematic during use, but the following critical warning is Chuxian when the Web app is hot-deployed in Tomcat6, and if this warning is ignored, it causes Tomcat PermGen space OOM after frequent reload.
June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to had started a thread named [Scheduler_worker-1] but had failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to had started a thread named [Scheduler_worker-2] but had failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to had started a thread named [scheduler_worker-3] but had failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to has started a thread named [scheduler_worker-4] but Have failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to had started a thread named [Scheduler_worker-5] but had failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM org.apache.catalina.loader.WebappClassLoader clearreferencesthreadssevere:the Web Application [ /feeder# #1.5.0] appears to had started a thread named [scheduler_worker-6] but had failed to stop it. This was very likely to create a memory leak. June, 5:14:38 AM Org.apache.catalina.loader.WebappClassLoader clearreferencesthreads
The cause of the problem
The reason is that the open Scheduler_worker thread is not closed. However, Spring's Schedulerfactorybean implements the Disposablebean interface, which means that the Web container executes destroy () when it is closed, and executes as follows:
/** * Shut down the Quartz Scheduler on Beans factory shutdown, * stopping all scheduled jobs. */ Public void throws schedulerexception { logger.info ("shutting down Quartz Scheduler"); this. Scheduler.shutdown (this. waitforjobstocompleteonshutdown); }
Indicates that the factory has done shutdown. So, the problem arises in the real execution of the Scheduler.shutdown (true).
It turns out that this is a quartz bug (see https://jira.terracotta.org/jira/browse/QTZ-192), after calling Scheduler.shutdown (true), Quartz checks that the thread does not wait for those worker threads to stop and ends.
Online says that the bug has been patched in the Quartz2.1 version, but I still have a problem with the 2.2.1 version.
Solution to the problem
So how to solve the problem, here is a less beautiful scenario: After calling Scheduler.shutdown (true), add a little sleep time, waiting for the worker thread to stop all. As follows:
1. The custom schedulerfactory inherits Spring's Schedulerfactorybean, overwriting destroy () to increase the delay.
Public classSchedulerfactorybeanwithshutdowndelayextendsSchedulerfactorybean {/*** A less attractive solution to quartz memory leaks: * Increase the delay after calling Scheduler.shutdown (true), waiting for the worker thread to end. */@Override Public voidDestroy ()throwsschedulerexception {Super. Destroy (); Try{Thread.Sleep (1000); } Catch(interruptedexception e) {Throw NewRuntimeException (e); } }}
2. Replace the Schedulerfactorybean in the spring configuration file with the custom factory.
<id= "Schedulerfactory" class= " Com.sinosoft.ms.task.SchedulerFactoryBeanWithShutdownDelay "></Bean >
OK, problem solved.
Resolving the problem of memory leak when integrating Spring3.2.11 and Quartz2.2.1