此案例研究還將證明掌握線程轉儲分析技能的重要性; 包括用于IBM JVM Thread Dump格式。
環境規格
– Java EE服務器:Oracle Weblogic Server 11g和Spring 2.0.5 –操作系統:AIX 5.3 – Java虛擬機:IBM JRE 1.6.0 –平臺類型:門戶和訂購應用程序
監控和故障排除工具
– JVM線程轉儲(IBM JVM格式)– Compuware Server Vantage(Weblogic JMX監視和警報)
問題概述
從Compuware Server Vantage觀察到并報告了一個嚴重的線程阻塞問題,該問題影響了我們的2臺Weblogic 11g生產托管服務器,從而導致了最終用戶的應用程序影響和超時情況。
事實的收集和驗證
像往常一樣,Java EE問題調查需要收集技術和非技術事實,因此我們可以得出其他事實和/或就根本原因進行結論。 在采取糾正措施之前,要對以下事實進行核實,以便得出根本原因:
·對客戶有什么影響? MEDIUM(16個中只有2個受管服務器/ JVM受影響)·受影響平臺的最新更改? 是(新的與JMS相關的異步組件)·最近到受影響平臺的流量有增加嗎? 否·這個問題如何表現出來? 觀察到線程突然增加,導致線程快速耗盡。·Weblogic托管服務器重新啟動是否解決了問題? 是的,但是幾個小時后問題又回來了(不可預測的間歇性模式)
– 結論1 :
該問題與間歇性卡住的線程行為有關,該行為當時僅影響少數Weblogic托管服務器
– 結論2 :
由于問題是斷斷續續的,因此不太可能出現全局根本原因,例如下游系統無響應
線程轉儲分析–第一遍
處理滯留的線程問題時,要做的第一件事是生成JVM線程轉儲。 無論您的環境規格和問題背景如何,這都是一條黃金法則。 JVM線程轉儲快照為您提供了有關活動線程及其當時正在執行的處理/任務類型的重要信息。
現在回到我們的案例研究,生成了一個IBM JVM線程轉儲(javacore.xyz格式),它確實揭示了以下Java線程死鎖情況:
1LKDEADLOCK Deadlock detected !!!NULL ---------------------NULL 2LKDEADLOCKTHR Thread '[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012CC08B00)3LKDEADLOCKWTR is waiting for:4LKDEADLOCKMON sys_mon_t:0x0000000126171DF8 infl_mon_t: 0x0000000126171E38:4LKDEADLOCKOBJ weblogic/jms/frontend/FESession@0x07000000198048C0/0x07000000198048D8: 3LKDEADLOCKOWN which is owned by:2LKDEADLOCKTHR Thread '[STUCK] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012E560500)3LKDEADLOCKWTR which is waiting for:4LKDEADLOCKMON sys_mon_t:0x000000012884CD60 infl_mon_t: 0x000000012884CDA0:4LKDEADLOCKOBJ weblogic/jms/frontend/FEConnection@0x0700000019822F08/0x0700000019822F20: 3LKDEADLOCKOWN which is owned by:2LKDEADLOCKTHR Thread '[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012CC08B00)
死鎖情況可以按照以下方式進行翻譯:
– Weblogic線程8正在等待獲取Weblogic線程10擁有的對象監視器鎖
– Weblogic線程#10正在等待獲取Weblogic線程#8擁有的對象監視器鎖 結論: Weblogic線程#8和#10都在等待。 永遠! 現在,在深入分析根本原因之前,讓我為您提供有關Java Thread死鎖的高級概述。
Java線程死鎖概述
你們中的大多數人可能都熟悉Java Thread死鎖原理,但是您真的遇到了真正的死鎖問題嗎?
根據我的經驗,真正的Java死鎖很少見,在過去的10年中,我只看到5次此類死鎖。 原因是大多數與線程卡住有關的問題是由于線程掛起情況(正在等待遠程IO調用等)引起的,而與其他線程沒有真正的死鎖條件有關。
Java線程死鎖是一種情況,例如,線程A等待獲取線程B持有的對象監視器鎖,而該線程本身正等待獲取線程A持有的對象監視器鎖。這兩個線程將永遠彼此等待。 這種情況可以如下圖所示:

線程死鎖已確認……現在該怎么辦?
一旦確認了死鎖( 大多數JVM線程轉儲實現將為您突出顯示 ),下一步就是通過檢查死鎖情況下涉及的每個線程及其當前任務和等待條件,來進行更深入的分析。
在我們的問題案例中,對于涉及死鎖條件的每個線程,在部分線程堆棧跟蹤下面找到: **請注意,出于保密目的,真實應用程序Java包名稱已重命名**
Weblogic線程#8
'[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' J9VMThread:0x000000012CC08B00, j9thread_t:0x00000001299E5100, java/lang/Thread:0x070000001D72EE00, state:B, prio=1(native thread ID:0x111200F, native priority:0x1, native policy:UNKNOWN)Java callstack:at weblogic/jms/frontend/FEConnection.stop(FEConnection.java:671(Compiled Code))at weblogic/jms/frontend/FEConnection.invoke(FEConnection.java:1685(Compiled Code))at weblogic/messaging/dispatcher/Request.wrappedFiniteStateMachine(Request.java:961(Compiled Code))at weblogic/messaging/dispatcher/DispatcherImpl.syncRequest(DispatcherImpl.java:184(Compiled Code))at weblogic/messaging/dispatcher/DispatcherImpl.dispatchSync(DispatcherImpl.java:212(Compiled Code))at weblogic/jms/dispatcher/DispatcherAdapter.dispatchSync(DispatcherAdapter.java:43(Compiled Code))at weblogic/jms/client/JMSConnection.stop(JMSConnection.java:863(Compiled Code))at weblogic/jms/client/WLConnectionImpl.stop(WLConnectionImpl.java:843)at org/springframework/jms/connection/SingleConnectionFactory.closeConnection(SingleConnectionFactory.java:342)at org/springframework/jms/connection/SingleConnectionFactory.resetConnection (SingleConnectionFactory.java:296)at org/app/JMSReceiver.receive()……………………………………………………………………
Weblogic線程#10
'[STUCK] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'' J9VMThread:0x000000012E560500, j9thread_t:0x000000012E35BCE0, java/lang/Thread:0x070000001ECA9200, state:B, prio=1(native thread ID:0x4FA027, native priority:0x1, native policy:UNKNOWN)Java callstack:at weblogic/jms/frontend/FEConnection .getPeerVersion(FEConnection.java:1381(Compiled Code))at weblogic/jms/frontend/FESession.setUpBackEndSession(FESession.java:755(Compiled Code))at weblogic/jms/frontend/FESession.consumerCreate(FESession.java:1025(Compiled Code))at weblogic/jms/frontend/FESession.invoke(FESession.java:2995(Compiled Code))at weblogic/messaging/dispatcher/Request.wrappedFiniteStateMachine(Request.java:961(Compiled Code))at weblogic/messaging/dispatcher/DispatcherImpl.syncRequest(DispatcherImpl.java:184(Compiled Code))at weblogic/messaging/dispatcher/DispatcherImpl.dispatchSync(DispatcherImpl.java:212(Compiled Code))at weblogic/jms/dispatcher/DispatcherAdapter.dispatchSync(DispatcherAdapter.java:43(Compiled Code))at weblogic/jms/client/JMSSession.consumerCreate(JMSSession.java:2982(Compiled Code))at weblogic/jms/client/JMSSession.setupConsumer(JMSSession.java:2749(Compiled Code))at weblogic/jms/client/JMSSession.createConsumer(JMSSession.java:2691(Compiled Code))at weblogic/jms/client/JMSSession.createReceiver(JMSSession.java:2596(Compiled Code))at weblogic/jms/client/WLSessionImpl.createReceiver(WLSessionImpl.java:991(Compiled Code))at org/springframework/jms/core/JmsTemplate102.createConsumer(JmsTemplate102.java:204(Compiled Code))at org/springframework/jms/core/JmsTemplate.doReceive(JmsTemplate.java:676(Compiled Code))at org/springframework/jms/core/JmsTemplate$10.doInJms(JmsTemplate.java:652(Compiled Code))at org/springframework/jms/core/JmsTemplate.execute(JmsTemplate.java:412(Compiled Code))at org/springframework/jms/core/JmsTemplate.receiveSelected(JmsTemplate.java:650(Compiled Code))at org/springframework/jms/core/JmsTemplate.receiveSelected(JmsTemplate.java:641(Compiled Code))at org/app/JMSReceiver.receive()……………………………………………………………
如您在上面的“線程跟蹤跟蹤”中所看到的,這種死鎖確實來自我們的應用程序代碼,該應用程序代碼使用Spring框架API進行JMS使用者實現(在不使用MDB的情況下非常有用)。 堆棧跟蹤非常有趣,它揭示了兩個線程都在與相同的 Weblogic JMS使用者會話/連接競爭的情況下,并導致死鎖情況:
– Weblogic線程#8試圖重置并關閉當前的JMS連接– Weblogic線程#10試圖使用相同的JMS連接/會話以創建新的JMS使用方–觸發了線程死鎖!
根本原因:非線程安全的Spring JMS SingleConnectionFactory實現
Spring JIRA錯誤數據庫的代碼回顧和快速研究確實揭示了以下與上述分析完美相關的以下線程安全缺陷:
#SingleConnectionFactory的resetConnection導致與基礎OracleAQ的JMS連接的死鎖https://jira.springsource.org/browse/SPR-5987
Spring SingleConnectionFactory的修補程序于2009年發布,確實涉及添加適當的sync {}塊,以防止在發生JMS Connection重置操作時線程死鎖:
synchronized (connectionMonitor) {//if condition added to avoid possible deadlocks when trying to reset the target connection if (!started) {this.target.start(); started = true; }}
解
我們的團隊目前正計劃將該Spring補丁不久后集成到我們的生產環境中。 在我們的測試環境中執行的初始測試是肯定的。
結論
我希望這個案例研究能幫助您理解現實中的Java線程死鎖問題,以及適當的線程轉儲分析技能如何使您能夠在代碼級快速查明與線程相關的卡住問題的根本原因。 請不要猶豫,發表任何評論或問題。
參考: Java Thread死鎖–來自我們的JCG合作伙伴 Pierre-Hugues Charbonneau的案例研究 ,位于Java EE支持模式和Java教程博客上。
翻譯自: https://www.javacodegeeks.com/2012/06/java-thread-deadlock-case-study.html