"Tomcat+Spring+Quartz"解決方案下,關閉Tomcat出現"執行緒未關閉,出現記憶體洩漏"錯誤
阿新 • • 發佈:2019-02-19
使用"Tomcat+Spring+Quartz"解決方案,在關閉Tomcat時出現如圖1所示錯誤資訊:
圖1
這裡使用的Tomcat版本為6.2.32,Spring版本為3.2.3,Quartz版本為1.8.6
一、原因分析在"Tomcat+Spring+Quartz"的解決方案中,Tomcat在執行的時候,Spring中配置的Quartz SchedulerFactoryBean會建立多個工作執行緒。在Tomcat關閉的時候,檢測到這些執行緒沒有得到及時的銷燬,因而出現如圖1的錯誤。
二、解決方案
Tomcat關閉的時候,會去銷燬Spring例項。根據Spring的生命週期機制,在銷燬Spring例項的過程中,會去處理Spring中配置的Bean例項的銷燬事宜。只要Spring中配置的Bean例項符合以下圖2中的任意一個條件
圖2
在我們的專案中,在Spring中配置Quartz SchedulerFactoryBean的片段如下:
檢視org.springframework.scheduling.quartz.SchedulerFactoryBean的原始碼,發現它繼承org.springframework.beans.factory.DisposableBean介面,滿足圖2中的條件。因而在Spring例項的銷燬過程中,SchedulerFactoryBean例項的destroy()方法會被自動呼叫。<bean id="startQuartz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="doTime"/> </list> </property> </bean>
因而網上很多“使呼叫SchedulerFactoryBean例項的銷燬方法”的解決方案都是錯誤的,這些解決方案包括“在Spring中定義SchedulerFactoryBean例項時增加‘destroy-method’屬性”,“實現一個監聽器,使得在Tomcat銷燬時去呼叫SchedulerFactoryBean例項的銷燬方法”等。
實際上,產生以上錯誤的原因是在銷燬過程中,SchedulerFactoryBean例項的destroy()方法的執行會給它建立的執行緒傳送關閉指令,然後destroy()方法就執行完畢返回。但是其實這些執行緒真正關閉還是需要一定時間的,這產生了時延,因此當destroy()方法執行完畢返回,然後相應的Spring例項的銷燬過程完成之後,Tomcat認為理應沒有執行緒在執行,但是卻發現還有執行緒在執行,因此就會丟擲以上錯誤。
那麼一種解決方案就是讓SchedulerFactoryBean例項的destroy()方法等待一段時間再完成返回,相應的Spring例項的銷燬過程也會等待該段時間,在這段時間內這些Quartz SchedulerFactoryBean的執行緒得以關閉,接著Tomcat檢查的時候就不會丟擲還有執行緒在執行出現記憶體洩漏的錯誤。
我建立瞭如下類:
相應在Spring中的配置片段如下:package com.dslztx.utils; import org.quartz.SchedulerException; import org.springframework.scheduling.quartz.SchedulerFactoryBean; public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean { @Override public void destroy() throws SchedulerException { super.destroy(); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }
<bean id="startQuartz" lazy-init="false" autowire="no" class="com.dslztx.utils.SchedulerFactoryBeanWithShutdownDelay">
<property name="triggers">
<list>
<ref bean="doTime"/>
</list>
</property>
</bean>
三、其他
1、由於這些Quartz SchedulerFactoryBean建立的執行緒最後能夠得以關閉,因此Tomcat程序也能被正常關閉,在有些情形中,Tomcat程序內的執行緒不能得以關閉,那麼Tomcat程序也不能被正常關閉,只能通過kill命令,強行殺死程序。
2、這個問題已經在Quartz 2.1上修復
參考文獻:
[1]http://forums.terracotta.org/forums/posts/list/3479.page[2]http://stackoverflow.com/questions/2730354/spring-scheduler-shutdown-error
[3]https://jira.terracotta.org/jira/browse/QTZ-192
[4]http://forum.spring.io/forum/spring-projects/container/25869-quartz-doesn-t-shutdown