javacore分析工具_Javacore 与 WebSphere Commerce 性能问题之争?谁才是赢家
近年來,依據(jù) WebSphere Commerce(以下簡稱為 WC)搭建的電子商務(wù)網(wǎng)站系統(tǒng)日益增多。由于系統(tǒng)本身的復(fù)雜性,一旦系統(tǒng)出現(xiàn)問題,尤其是性能問題,問題診斷和定位就會非常困難。下圖所示為由 WC 系統(tǒng)為核心搭建的電子商務(wù)網(wǎng)站的一般邏輯架構(gòu) , 如圖 1 所示:
圖 1. 電子商務(wù)網(wǎng)站的一般邏輯架構(gòu)
在整個(gè)系統(tǒng)架構(gòu)中,核心應(yīng)用邏輯運(yùn)行在 WebSphere 應(yīng)用服務(wù)器上。當(dāng)系統(tǒng)出現(xiàn)性能問題時(shí),雖然實(shí)際問題的根源可能分布在各個(gè)節(jié)點(diǎn)上,但是通過在應(yīng)用服務(wù)器上的線程運(yùn)行狀態(tài)分析,都有助于對整個(gè)系統(tǒng)的當(dāng)前狀態(tài)以及可能的問題根源進(jìn)行快速定位。WebSphere 應(yīng)用服務(wù)器運(yùn)行在 JVM(Java 虛擬機(jī))之上。所以掌握通過 Javacore 進(jìn)行 Java 線程分析,對于解決 WC 系統(tǒng)中的性能問題至關(guān)重要。
本文介紹如何通過 Javacore 文件分析線程運(yùn)行狀態(tài),以及輔助分析工具的使用。并通過實(shí)例講解如何利用 Java 線程分析解決 WC 系統(tǒng)中的性能問題。本文所介紹的分析方法對于其他基于 JavaEE 構(gòu)建的企業(yè)應(yīng)用也有借鑒意義。
如何通過 Javacore 了解線程運(yùn)行狀況
Javacore 是一個(gè)當(dāng)前 JVM 運(yùn)行狀態(tài)的快照。通過對 Javacore 的分析,可以了解在 JVM 中運(yùn)行的應(yīng)用程序的當(dāng)前狀態(tài),比如是否“卡”在某一點(diǎn)上,或在某些代碼上運(yùn)行時(shí)間太長。
Javacore 的基本內(nèi)容
Javacore,也可以稱為“threaddump”或是“javadump”,它是 Java 提供的一種診斷特性,能夠提供一份可讀的當(dāng)前運(yùn)行的 JVM 中線程使用情況的快照。即在某個(gè)特定時(shí)刻,JVM 中有哪些線程在運(yùn)行,每個(gè)線程執(zhí)行到哪一個(gè)類,哪一個(gè)方法。
應(yīng)用程序如果出現(xiàn)不可恢復(fù)的錯(cuò)誤或是內(nèi)存泄露,就會自動(dòng)觸發(fā) Javacore 的生成。而為了性能問題診斷的需要,我們也會主動(dòng)觸發(fā)生成 Javacore。在 AIX、Linux、Solaris 環(huán)境中,我們通常使用 kill -3 <PID> 產(chǎn)生該進(jìn)程的 Javacore。IBM Java6 中產(chǎn)生 Javacore 的詳細(xì)方法。
對于 IBM JVM,AIX 平臺上的 Javacore 會被寫到 javacore.<date>.<time>.<PID>.<sequence>.txt 中。對于 Oracle JVM,Javacore 被附加到 native_stdout.txt。Javacore 的內(nèi)容有兩列,第一列是“類型”,第二列表示“數(shù)據(jù)”,如清單 1 所示:
清單 1
- 1TISIGINFO Dump Event "user" (00004000) received
- 1TIDATETIME Date: 2013/12/22 at 23:05:18
- 1TIFILENAME Javacore filename:
- /usr/WebSphere/AppServer/profiles/demo_solr/javacore.20131222.230518.7995516.0004.txt
通常情況下,Javacore 中除了線程信息外,還能提供關(guān)于操作系統(tǒng),應(yīng)用程序環(huán)境,線程,程序調(diào)用棧,鎖,監(jiān)視器和內(nèi)存使用等相關(guān)信息。
為了便于分析,Javacores 的每一段的開頭,都會用“----------”和上一信息塊區(qū)分開來。每一信息塊的標(biāo)題會以“=========”來標(biāo)識,很容易找到,如清單 2 所示:
清單 2
- NULL ------------------------------------------------------------------------
- 0SECTION GPINFO subcomponent dump routine
- NULL ================================
- 2XHOSLEVEL OS Level : AIX 7.1
- 2XHCPUS Processors -
- 3XHCPUARCH Architecture : ppc64
- 3XHNUMCPUS How Many : 8
- 3XHNUMASUP NUMA is either not supported or has been disabled by user
- NULL
- 1XHERROR2 Register dump section only produced for SIGSEGV, SIGILL or SIGFPE.
- NULL
Javacore 文件中,每行都包含一個(gè)標(biāo)簽,這個(gè)標(biāo)簽最多由 15 個(gè)字符組成。第一位數(shù)字表示信息的詳細(xì)級別,級別越高代表信息越詳細(xì)。接著的兩個(gè)字符是段標(biāo)題的縮寫,例如:“CI”表示 Command line interpreter,“CL”表示 Class loader,“LK”表示 Locking,“ST”表示 Storage,“TI”表示 Title,“XE”表示 Execution engine 等。余下的字符表示信息的概述。如下清單 3 所示:
清單 3
- 3XMTHREADINFO "Thread-18" J9VMThread:0x00000000308DA900,
- j9thread_t:0x0000010016C4F2E0, java/lang/Thread:0x000000004136E3E8, state:P, prio=5
雖然不同版本的 JVM 所產(chǎn)生的 Javacore 的格式會稍有不同,但基本都包含下面幾個(gè)內(nèi)容:
TITLE 信息塊:描述 Javacore 產(chǎn)生的原因,時(shí)間以及文件的路徑。常見的 Javacore 產(chǎn)生的原因可以參考文章 [2]。最常見的有下面三種:
- user:SIGQUIT 信號
- gpf:程序一般保護(hù)性錯(cuò)誤導(dǎo)致系統(tǒng)崩潰
- systhrow:JVM 內(nèi)部拋出的異常
- GPINFO 信息塊:GPF(一般保護(hù)性錯(cuò)誤)信息
- ENVINFO 信息塊:系統(tǒng)運(yùn)行時(shí)的環(huán)境和 JVM 參數(shù)
- MEMINFO 信息塊:內(nèi)存使用情況和垃圾回收情況
- LOCKS 信息塊:用戶監(jiān)視器(monitor)和系統(tǒng)監(jiān)視器(monitor)情況
- THREADS 信息塊:所有 java 線程的狀態(tài)信息和執(zhí)行堆棧
- CLASSES 信息塊:類加載信息
Javacore 中的線程可分為以下幾種狀態(tài):
- 死鎖(Deadlock)【重點(diǎn)關(guān)注】:一般指多個(gè)線程調(diào)用間,進(jìn)入相互資源占用,導(dǎo)致一直等待無法釋放的情況。
- 執(zhí)行中(Runnable)【重點(diǎn)關(guān)注】:一般指該線程正在執(zhí)行狀態(tài)中,該線程占用了資源,正在處理某個(gè)請求,有可能在對某個(gè)文件操作,有可能進(jìn)行數(shù)據(jù)類型等轉(zhuǎn)換等。
- 等待資源(Waiting on condition)【重點(diǎn)關(guān)注】:等待資源,如果堆棧信息明確是應(yīng)用代碼,則證明該線程正在等待資源,一般是大量讀取某資源、且該資源采用了資源鎖的情況下,線程進(jìn)入等待狀態(tài)。又或者,正在等待其他線程的執(zhí)行等。
- 等待監(jiān)控器檢查資源(Waiting on monitor)
- 暫停(Suspended)
- 對象等待中(Object.wait())
- 阻塞(Blocked)【重點(diǎn)關(guān)注】:指當(dāng)前線程執(zhí)行過程中,所需要的資源長時(shí)間等待卻一直未能獲取到,被容器的線程管理器標(biāo)識為阻塞狀態(tài),可以理解為等待資源超時(shí)的線程。這種情況在應(yīng)用的日志中,一般可以看到 CPU 饑渴,或者某線程已執(zhí)行了較長時(shí)間的信息。
- 停止(Parked)
通過對 Javacore 數(shù)據(jù)的分析經(jīng)驗(yàn),結(jié)合對具體應(yīng)用代碼邏輯的理解,有經(jīng)驗(yàn)的工程師可以直接通過文本編輯器查看原始 Javacore 文件來分析當(dāng)前應(yīng)用程序的運(yùn)行狀態(tài)。一般初學(xué)者則需要通過一些工具進(jìn)行更直觀的分析。
圖形化分析工具 TMDA
有很多圖形化工具可以用于 Javacore 的分析。筆者常用的工具為:IBM Thread and Monitor Dump Analyzer for Java 工具,簡稱 TMDA。TMDA 提供以下功能:
- 提供一個(gè)簡潔的 Javacore 內(nèi)容的總結(jié),包括一些初步的預(yù)警信息,線程柱狀圖,內(nèi)存使用情況信息等等。
- 方便地分析線程棧和監(jiān)視器(monitor)的用戶接口
- 方便地進(jìn)行多個(gè) Javacore 中的線程棧和監(jiān)視器進(jìn)行比較的用戶接口
無論是初學(xué)者還是性能分析專家都可以使用 TMDA 進(jìn)行 Javacore 的快速分析。關(guān)于 TMDA 的更多介紹可以參考它的社區(qū).
WC 線程執(zhí)行堆棧分析
不論是否利用 TMDA 工具進(jìn)行分析,對 Javacore 的分析最終都會落實(shí)在具體線程的執(zhí)行堆棧上。如果對具體應(yīng)用的代碼不熟悉,那么看著一個(gè)個(gè)長長的執(zhí)行堆棧,可能會覺得無從下手。本部分介紹 WC 線程執(zhí)行堆棧的常見代碼和對應(yīng)的功能模塊。初學(xué)者可以根據(jù)這些示例推測某個(gè)線程的當(dāng)前運(yùn)行狀態(tài)。需要注意的是,WC 的不同版本,同樣的功能模塊的具體代碼可能會發(fā)生變化,經(jīng)過用戶定制的代碼就更是千差萬別。本部分提供的只是依據(jù) WC FEP7 版本代碼的一些示例,讀者需要根據(jù)自己所處理的系統(tǒng)的實(shí)際代碼情況靈活掌握,不可拘泥。
通常一個(gè) Javacore 里面會有上百個(gè)線程,這些線程的地位并不一樣。有些線程是系統(tǒng)運(yùn)行的“入口線程”,而其他一些線程只是由這些線程派生出來的輔助線程。所以 Javacore 分析過程中一定要抓住這些主要線程。
WC 的核心是一個(gè) Web 應(yīng)用,所以大部分 WC 的 Javacore 以應(yīng)用服務(wù)器的 Web 容器為入口。用來處理前臺商店或后臺管理端的 JVM 基本類似,但專門運(yùn)行定時(shí)任務(wù)的 JVM 會有所區(qū)別。下圖 2 所示為以 Web 容器為入口線程的一般調(diào)用結(jié)構(gòu):
圖 2. 調(diào)用結(jié)構(gòu)
從 Web 容器入口開始,一般會進(jìn)入 Servlet 執(zhí)行。如果有緩存而且命中的話,則會進(jìn)入 DynaCache 的相關(guān)代碼。如果無緩存或緩存不命中,如果是 JSP 頁面,則會執(zhí)行 JSP 的相關(guān)代碼,否則會執(zhí)行相應(yīng)的邏輯。代碼邏輯處理過程中,經(jīng)常會訪問到數(shù)據(jù)庫(通過 DSL 或 EJB)或 Solr 搜索(通過 BOD 或 REST),還有可能訪問到外部系統(tǒng)集成接口(通過 HTTP 同步調(diào)用或消息隊(duì)列)。如果數(shù)據(jù)庫服務(wù)器 / 搜索服務(wù)器 / 系統(tǒng)集成服務(wù)器分布在其他節(jié)點(diǎn)上,那么這些調(diào)用最終都會轉(zhuǎn)化為網(wǎng)絡(luò)訪問。
自下而上的關(guān)鍵代碼:
Web 容器處理請求 Package 名 / 類名 . 方法名:com.ibm.ws.webcontainer/WebContainer.handleRequest RuntimeFilter Package 名 / 類名 . 方法名:com.ibm.commerce.webcontroller/RuntimeServletFilter.doFilterAction Servlet 處理 Package 名 / 類名 . 方法名:com.ibm.commerce.struts/ECActionServlet.doGet 或者 Package 名 / 類名 . 方法名:com.ibm.commerce.struts/ECActionServlet.doPost JSP 處理 Package 名 / 類名 . 方法名:com.ibm._jsp/_(JSP 文件名 )._jspService Command 執(zhí)行(圖例為 BOD command) Package 名 / 類名 . 方法名:com.ibm.commerce.*/Abstract(*)CmdImpl.performExecute DSL Package 名 / 類名 . 方法名:com.ibm.commerce.foundation.server.services.dataaccess/AbstractDataServiceFacade.* JDBC 查詢 Package 名 / 類名 . 方法名:com.ibm.ws.rsadapter.jdbc/WSJdbcPreparedStatement.executeQuery 或 Package 名 / 類名 . 方法名:com.ibm.ws.rsadapter.cci/WSResourceAdapterBase.executeQuery 更新 Package 名 / 類名 . 方法名:com.ibm.ws.rsadapter.jdbc/WSJdbcPreparedStatement.executeUpdate 或 Package 名 / 類名 . 方法名:com.ibm.ws.rsadapter.cci/WSResourceAdapterBase.executeUpdate 數(shù)據(jù)庫驅(qū)動(dòng)代碼 DB2 Package 名:com.ibm.db2.* Oracle Package 名 / 類名 . 方法名:oracle.jdbc.driver/OraclePreparedStatement.executeInternal 外部網(wǎng)絡(luò)訪問 (網(wǎng)絡(luò)讀)Package 名 / 類名 . 方法名:java.net/SocketInputStream.socketRead0 (網(wǎng)絡(luò)寫)Package 名 / 類名 . 方法名:java.net/SocketOutputStream.socketWrite0其他常見的執(zhí)行堆棧舉例:
· 空閑(Idle)
這樣的堆棧表示當(dāng)前線程處于空閑狀態(tài)(對于 Web 容器而言,即當(dāng)前線程沒有接收到 Web 請求)。其調(diào)用棧為:
- Package 名 / 類名 . 方法名:java.lang/Object/wait
- …
- Package 名 / 類名 . 方法名:com.ibm.ws.util/ThreadPool$Worker. run
- 或者
- Package 名 / 類名 . 方法名:com.ibm.io.async/AsyncLibrary. aio_getioev*
- …
- Package 名 / 類名 . 方法名:com.ibm.ws.util/ThreadPool$Worker. run
事務(wù)(Transaction)處理
- 提交(Commit)
- Package 名 / 類名 . 方法名:com.ibm.commerce.server/TransactionManager.commit
- 回滾(Rollback)
- Package 名 / 類名 . 方法名:com.ibm.commerce.server/TransactionManager.rollback
緩存(DynaCache)讀取
- Package 名 / 類名 . 方法名:com.ibm.ws.cache/Cache.getEntry(或 getCacheEntry)
- 基于 WXS 的緩存則為
- Package 名 / 類名 . 方法名:com.ibm.ws.objectgrid.dynacache/RemoteCoreCacheImpl.get
MultiClick 處理
- Package 名 / 類名 . 方法名:com.ibm.commerce.webcontroller.doubleclick/MultiClickRequestHandler. waitForResponse
消息處理(MQ)
- Package 名 / 類名 . 方法名:com.ibm.mq.jmqi.remote.internal/RemoteRcvThread.run
搜索(Solr)處理
- Package 名 / 類名 . 方法名:org.apache.solr.client.solrj/SolrServer.query
REST 處理
- Package 名 / 類名 . 方法名:com.ibm.commerce.foundation.internal.client.util/RESTHandler.execute
案例分析
本部分通過兩個(gè)具體實(shí)例講解如何通過 Javacore 分析來分析解決 WC 系統(tǒng)中的性能問題。當(dāng)系統(tǒng)出現(xiàn)性能問題的時(shí)候,通常系統(tǒng)的工作狀態(tài)會發(fā)生某些異常。我們的任務(wù)就是通過 Javacore 分析來找出系統(tǒng)關(guān)鍵線程的運(yùn)行狀態(tài)變化。這里一般都需要獲取多次 Javacore 并進(jìn)行比較,發(fā)現(xiàn)哪些是“變”的部分,哪些是“不變”的部分。所以,誰和誰比,比什么,是分析問題的關(guān)鍵。
邏輯死鎖問題
Javacore 分析經(jīng)常用于解決邏輯死鎖問題。使用 TMDA 工具,可以在圖形界面中快速找出不同線程之間的等待關(guān)系,
如果在同一個(gè) JVM 內(nèi)部出現(xiàn)了線程之間循環(huán)等待的狀況,就會進(jìn)入線程之間的死鎖(Deadlock)狀態(tài)。對于未定制的 WC 系統(tǒng)而言,直接出現(xiàn)這樣的死鎖問題的可能性比較小。較常見的是在整個(gè)系統(tǒng)的不同節(jié)點(diǎn)之間出現(xiàn)的邏輯死鎖問題,這種問題一般不能直接在 Javacore 中識別出來。本節(jié)通過一個(gè) WC 內(nèi)部測試過程中遇到的案例介紹如何分析解決這種邏輯死鎖問題。
這是一個(gè)隨機(jī)瀏覽產(chǎn)品目錄的測試場景。在這個(gè)場景中,WC 應(yīng)用服務(wù)器接收請求,并調(diào)用搜索服務(wù)器上的索引進(jìn)行處理。簡化后的系統(tǒng)架構(gòu)圖(省略了 Web 服務(wù)器)如圖 3 所示:
圖 3. 簡化的系統(tǒng)架構(gòu)圖
這里,搜索服務(wù)器在處理過程中會有一次回調(diào)到應(yīng)用服務(wù)器,通過應(yīng)用服務(wù)器獲取某些搜索所需的數(shù)據(jù)。
我們對這個(gè)測試場景進(jìn)行了用戶數(shù)遞增的壓力測試,正常情況下,我們期望看到的結(jié)果是隨著用戶數(shù)的增加,系統(tǒng)的吞吐量逐漸上升,最后達(dá)到一個(gè)平穩(wěn)狀態(tài)(達(dá)到 CPU 瓶頸)。但是實(shí)際測試過程中看到的現(xiàn)象卻是,當(dāng)并發(fā)用戶數(shù)增加到某個(gè)數(shù)值的時(shí)候,系統(tǒng)吞吐率突然下降,最終降低到 0
此時(shí),我們監(jiān)控兩個(gè)節(jié)點(diǎn)上的 CPU 使用狀況。發(fā)現(xiàn)吞吐量降低為 0 的同時(shí),CPU 使用率也幾乎降低為 0。因此,初步可以判斷系統(tǒng)出現(xiàn)了邏輯死鎖問題。反之,如果某個(gè)節(jié)點(diǎn)上的 CPU 使用率(或其他資源使用率)很高,則有可能是邏輯死循環(huán)或其他代碼實(shí)現(xiàn)問題。
要想進(jìn)一步分析就需要通過 Javacore 分析獲取當(dāng)前 JVM 的運(yùn)行狀態(tài)信息。這里我們需要分別在應(yīng)用服務(wù)器和搜索服務(wù)器端獲取 Javacore(采樣的時(shí)間點(diǎn)要同步)。采樣點(diǎn)可以做三次,在吞吐率下降前采樣一次,下降后采樣兩次(中間間隔 30 秒以上)。然后對這三次 Javacore 進(jìn)行對比分析。
在 TMDA 中打開采樣到的 Javacore 文件。TMDA 提供的“Compare Threads”功能可以用來比較這些 Javacore 文件:
先來看 WC 應(yīng)用服務(wù)器端的 Javacore 文件。前文已經(jīng)解釋過 Web 容器通常是整個(gè)應(yīng)用的入口,所以我們重點(diǎn)關(guān)注 Web 容器相關(guān)線程的狀態(tài)。TMDA 比較的結(jié)果如圖 12 所示:
用黃底色表示(TMDA 的不同版本顯示效果可能略有區(qū)別)的線程表示在兩次采樣中的狀態(tài)相同(執(zhí)行堆棧相同)。這里可以很清楚地看到在第二次和第三次采樣點(diǎn)上,所有 Web 容器的線程執(zhí)行狀態(tài)都相同,系統(tǒng)已經(jīng)進(jìn)入了假死(HANG)狀態(tài)(注意這兩次采樣發(fā)生在系統(tǒng)吞吐率降為 0 之后)。
這里需要注意,在第一個(gè)采樣點(diǎn)(系統(tǒng)吞吐率下降之前),還有很多線程處于“阻塞”(Blocked)狀態(tài)。而在第二、第三個(gè)采樣點(diǎn)上,除了個(gè)別線程處于阻塞狀態(tài)外,多數(shù)線程都處于“執(zhí)行中”(Runnable)狀態(tài)。由此可見,不能完全依賴 Javacore 中標(biāo)明的線程狀態(tài)來判斷當(dāng)前系統(tǒng)的狀態(tài),關(guān)鍵還是要看執(zhí)行堆棧中實(shí)際在執(zhí)行的代碼。處于“執(zhí)行中”狀態(tài)的線程可能實(shí)際在等待,而處于“等待資源”(Waiting on condition)狀態(tài)的線程可能實(shí)際是“執(zhí)行中”狀態(tài)。
我們進(jìn)一步分析第二、第三個(gè)采樣點(diǎn)上,Web 容器各線程的執(zhí)行堆棧。我們發(fā)現(xiàn),雖然下層處理的頁面(JSP)各有不同,但是頂層處于運(yùn)行中的代碼都一樣:
基本上所有的 Web 容器線程都在等待 REST 請求的網(wǎng)絡(luò)返回。根據(jù)前面描述的簡化后的系統(tǒng)結(jié)構(gòu)圖,可以推斷所有線程都在等候搜索服務(wù)器的處理結(jié)果。這是作者根據(jù)對系統(tǒng)結(jié)構(gòu)的理解進(jìn)行的判斷,如果讀者在實(shí)際問題分析過程中無法確定,可以使用 netstat 進(jìn)行網(wǎng)絡(luò)監(jiān)控,根據(jù) HTTP 鏈接的建立情況進(jìn)一步確認(rèn)。
下一步,我們再比較搜索服務(wù)器端的三個(gè)時(shí)間點(diǎn)上的 Javacores 文件。結(jié)果是類似的,同樣第二個(gè)和第三個(gè)采樣點(diǎn)上 Web 容器的所有線程都進(jìn)入了假死(HANG)狀態(tài):
我們再來看看搜索服務(wù)器端處于“執(zhí)行中”的線程都在干什么。基本上所有執(zhí)行堆棧的頂端都在執(zhí)行如下代碼:
經(jīng)過代碼分析,我們發(fā)現(xiàn)這是搜索服務(wù)器端在通過 REST 回調(diào) WC 應(yīng)用服務(wù)器,獲取 BCS(BusinessConextService)相關(guān)的數(shù)據(jù)。
這樣的調(diào)用關(guān)系為什么會導(dǎo)致邏輯上的死鎖呢?關(guān)鍵在于對 Web 容器的線程池資源的競爭上。每個(gè) WC 端接收到的請求在處理過程中需要占用 Web 容器的線程池的一個(gè)線程資源,而這個(gè)處理邏輯在處理過程中請求了搜索服務(wù)器端,又通過搜索服務(wù)器端回調(diào)到 WC 端,這就需要占用 Web 容器的線程池的另一個(gè)線程資源。
這個(gè)邏輯關(guān)系可以簡化為圖 4:
圖 4. 簡化邏輯關(guān)系圖
在應(yīng)用服務(wù)器的 Web 容器線程池資源上,通過搜索服務(wù)器的回調(diào)形成了一個(gè)閉環(huán)。這類似于標(biāo)準(zhǔn)的“哲學(xué)家就餐問題”,如果所有的請求都占用了第一個(gè)線程資源,而請求第二個(gè)線程資源,那么所有的線程都會阻塞在這個(gè)狀態(tài)上,形成死鎖。要解決這個(gè)線程死鎖問題,必須消除這個(gè)資源占用上的閉環(huán)。可以采取的方案包括消除從 Search 端的 REST 回調(diào),建立一個(gè)獨(dú)立的線程池專門負(fù)責(zé)處理 Search 端的回調(diào),等等。
尋找性能瓶頸
除了分析邏輯死鎖問題外,Javacore 分析也可以用于尋找性能瓶頸。一般來說,尋找代碼邏輯中的性能瓶頸,需要對代碼的執(zhí)行路徑進(jìn)行 Profiling(執(zhí)行統(tǒng)計(jì)分析)。Profiling 工具一般有兩種。能夠提供完整執(zhí)行堆棧的工具一般都是侵入式的,運(yùn)行開銷很高,并不適于在高負(fù)載的生產(chǎn)環(huán)境中使用。基于采樣(sampling)的工具運(yùn)行開銷很小,但通常都不提供完整的執(zhí)行堆棧。這里其實(shí)可以把 Javacore 分析當(dāng)作輔助的 Profiling 工具來用。每個(gè) Javacore 都提供了在某一時(shí)刻正在運(yùn)行的代碼的執(zhí)行堆棧,這可以看作一個(gè)采樣點(diǎn)。如果多做幾次采樣點(diǎn),那么根據(jù)這些 Javacore 數(shù)據(jù)就可以進(jìn)行一個(gè)執(zhí)行路徑的粗略 Profiling(不過總體采樣點(diǎn)數(shù)量比真正的 Profiling 工具少很多)。
我們?nèi)砸砸粋€(gè) WC 產(chǎn)品測試過程中遇到的問題為例介紹這種分析方法。
我們在某個(gè)產(chǎn)品開發(fā)的版本測試過程中發(fā)現(xiàn),搜索結(jié)果頁面(SearchResultDisplay)的性能比前一個(gè)版本下降了很多。為了找出性能下降的原因,我們對該頁面進(jìn)行了單場景壓力測試。當(dāng)系統(tǒng)性能進(jìn)入穩(wěn)定狀態(tài)后,我們在 WC 應(yīng)用服務(wù)器端做了 Javacore 數(shù)據(jù)采樣。
同樣,我們的入口線程仍是 Web 容器的線程。這里要解決的是性能下降問題(系統(tǒng) CPU 占用率很高),而不是邏輯死鎖問題。所以我們關(guān)注的重點(diǎn)是同一次采樣內(nèi)部線程之間的橫向比較,而不是多次采樣之間的橫向比較,所以只需要用到 TMDA 的線程分析功能,而不需要使用線程比較功能。
如果關(guān)注于執(zhí)行堆棧的頂部代碼,我們發(fā)現(xiàn) Web 容器各線程的執(zhí)行狀況比較分散,似乎沒有什么規(guī)律。但是如果從執(zhí)行堆棧的底部往上看,就會發(fā)現(xiàn)某些規(guī)律。這里我們發(fā)現(xiàn)在 Web 容器的 25 個(gè)線程(等于線程池的大小)中,有一半以上的執(zhí)行堆棧在執(zhí)行.
由于這是單場景壓力測試,基本上所有的 Web 容器線程都在執(zhí)行相同的 JSP:SearchBasedCategoryPage。如果這個(gè) JSP 里面沒有明顯的性能瓶頸,那么 Web 容器的 25 個(gè)線程應(yīng)該隨機(jī)運(yùn)行在這個(gè) JSP 的不同代碼邏輯之中。我們實(shí)際觀察到的現(xiàn)象則是有一半以上的線程都在執(zhí)行如下代碼:
com/ibm/commerce/infrastructure/facade/client/AbstractInfrastructureFacadeClient.getOnlineStore為了排除代碼執(zhí)行隨機(jī)性的影響,我們又在隨后的系統(tǒng)執(zhí)行過程中多做了幾次采樣,仍然得到了類似的結(jié)果。
這就表示該代碼有可能是整個(gè) JSP 邏輯中的性能瓶頸(當(dāng)然還不能完全確定)。另一方面,通過與前一個(gè)版本的 Javacore 進(jìn)行對比分析,發(fā)現(xiàn)前一版本的 Javacore 中并沒有出現(xiàn)該代碼。這說明該代碼是當(dāng)前版本新引入的代碼。通過進(jìn)一步的代碼分析發(fā)現(xiàn),這是在當(dāng)前版本中新加入的一段處理客戶定制邏輯的新代碼。我們屏蔽了該端代碼后,重新進(jìn)行了性能測試,發(fā)現(xiàn)性能基本上可以恢復(fù)到前一版本的水平。因此最終可以確定是這段代碼導(dǎo)致了當(dāng)前版本的性能下降問題。
如何解決這類性能問題呢?首先應(yīng)該是評估原始的實(shí)現(xiàn)邏輯,是否需要在每一個(gè)頁面請求的處理過程中執(zhí)行這段邏輯。如果不需要,則可以直接屏蔽該代碼段。如果這一段邏輯必須在每一個(gè)頁面請求中執(zhí)行,則可以考慮引入適當(dāng)?shù)木彺鏅C(jī)制,降低重復(fù)執(zhí)行時(shí)的開銷。
這種分析方法也可以用于在客戶生產(chǎn)系統(tǒng)中快速定位整個(gè)系統(tǒng)的性能瓶頸。生產(chǎn)系統(tǒng)執(zhí)行的頁面請求是多種多樣的,通常情況下,在生產(chǎn)系統(tǒng)上做 Javacore 應(yīng)該找不到什么規(guī)律。反之,如果多次采樣可以發(fā)現(xiàn)系統(tǒng)在高負(fù)載運(yùn)行狀態(tài)下,Web 容器線程的執(zhí)行堆棧存在某些規(guī)律,比如:大部分線程都在執(zhí)行目錄頁面(CategoryDisplay)顯示。而頁面訪問統(tǒng)計(jì)的結(jié)果顯示,目錄頁面的訪問頻率并不比其他頁面高很多。這種情況下就很有可能在 CategoryDisplay 頁面有性能瓶頸。下一步就可以進(jìn)行單場景的壓力測試來進(jìn)一步尋找 CategoryDisplay 頁面邏輯中的性能瓶頸。
如果能將 Javacore 分析的結(jié)果,與其他基于采樣的 Profiling 工具的分析結(jié)果相結(jié)合,則更容易快速找到代碼中的性能瓶頸。
總結(jié)
以上是生活随笔為你收集整理的javacore分析工具_Javacore 与 WebSphere Commerce 性能问题之争?谁才是赢家的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python桌面开发吐血_Python3
- 下一篇: python数据分析第七章实训3_《利用