此篇文章描述的症狀和上一篇文章一致(即執行tomcat ./shutdown.sh 後,雖然tomcat服務不能正常訪問了,但是ps -ef | grep java 後,發現tomcat對應的java進程未隨web容器關閉而銷燬,進而存在殭屍java進程),但是處理的過程不一致,所有又單開了一篇blog來寫。
我在另外一個項目中使用到了阿里的MetaQ消息中間件,然後shutdown tomcat 發現java進程依舊存在,沿用上一篇文章的思路,我最開始以爲是本地代碼中scheduledExecutorService沒有及時關閉,但check code後發現scheduledExecutorService 已經進行了shutdown處理。於是只能從jstack dump跟蹤,./jps 查詢到對應的pid,然後 ./jstack pid,發現存在如下一個非守護線程的dump:
"notify-remoting-ScanAllConnection-1-thread-1" prio=10 tid=0x00007f6124956000 nid=0x2cda waiting on condition [0x00007f6149544000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f04a5958> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1090)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:807)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1043)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1103)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
上述dump對應的代碼是Gecko中,com.taobao.gecko.service.impl.BaseRemotingController類,發現此類中存在一個ScheduledExecutorService scanAllConnectionExecutor,然後我竊以爲是此處未shutdown,但,非也,阿里coder的代碼不會出現如此低劣的漏洞的,遵循問題定位原則:出現bug時先確保不是自己的代碼出現問題,我又看了一遍項目中涉及到metaq的代碼,驚奇的發現,雖然一再強調MessageSessionFactory、MessageProducer、MessageConsumer 應該是單例複用形式存在,項目中我是採用spring來託管singleton的,然後,在創建MessageProducer時,卻沒有使用已經singleton的MessageSessionFactory,而是又重新new 出一個 MessageSessionFactory 實例,而且shutdown時只shutdown spring託管的實例,重新new 出來的對象並未對其進行shutdown。正是該原因,導致Gecko中的scanAllConnectionExecutor一直處於timed_waiting 狀態,進而導致jvm無法正常退出。
此次bug定位耗時近一天,最開始我甚至以爲是Gecko的bug,但事實證明,出問題往往是自己!引此爲戒 :)