I'm using quartz in my project. My web application has apparently caused a memory leak when it stops, the error is :
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.QuartzInitializerServlet
and org.quartz.ee.servlet.QuartzInitializerListener
. The code for my factory is:
StdSchedulerFactory factory = (StdSchedulerFactory) context.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY );
and settings for quartz in web.xml is :
<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 implementing org.quartz.InterruptableJob
you can properly interrupt threads triggered by servlet unloading.
@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 @DisallowConcurrentExecution
annotation.
In order for this to work the quartz property org.quartz.scheduler.interruptJobsOnShutdownWithWait
must 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.
Example quartz.properties
file:
org.quartz.scheduler.interruptJobsOnShutdownWithWait=true
Note that the interruption only is dispatched if the scheduler is configured to wait on shutdown, resulting in a call to scheduler.shutdown(true)
.