線程轉(zhuǎn)儲分析是用于分析基于Java的應(yīng)用程序中的性能瓶頸的傳統(tǒng)方法。在現(xiàn)代,我們有APM工具,可以提供各種指標(biāo)和屏幕來深入研究和識別性能問題,甚至在代碼級別也是如此。但對于某些性能問題或場合,線程轉(zhuǎn)儲分析仍然是識別瓶頸的最佳方法。
何時使用線程轉(zhuǎn)儲
要分析任何性能問題,最好在1到2秒的時間間隔內(nèi)進(jìn)行一系列線程轉(zhuǎn)儲。每隔1-2秒進(jìn)行10-15次線程轉(zhuǎn)儲,有助于分析陷入困境的線程或跨線程轉(zhuǎn)儲執(zhí)行相同的代碼。
l 線程轉(zhuǎn)儲可以在以下情況下進(jìn)行:
l 應(yīng)用程序掛起且沒有響應(yīng)
l 應(yīng)用程序需要時間才能響應(yīng)
l 運行應(yīng)用程序的服務(wù)器上CPU使用率高
l 活動線程數(shù)或線程總數(shù)的增加
線程轉(zhuǎn)儲有時也由應(yīng)用程序服務(wù)器自動生成。例如,WebSphere應(yīng)用程序服務(wù)器在OutOfMemoryError情況下生成線程轉(zhuǎn)儲,這有助于分析線程在該時刻的各種狀態(tài)。
對于場景#1和場景2,應(yīng)該將重點放在處于阻塞、停放/等待和可運行狀態(tài)的線程上。對于場景#3,重點應(yīng)該放在處于可運行狀態(tài)的線程上。無限循環(huán)執(zhí)行中的一些線程可能會導(dǎo)致高CPU使用率,查看可運行狀態(tài)可能有助于發(fā)現(xiàn)這一點。對于場景#4,應(yīng)該將重點放在處于可運行狀態(tài)和駐留/等待線程狀態(tài)的線程上。在所有場景中,忽略處于暫停或定時等待狀態(tài)的線程,這些線程正在等待執(zhí)行任務(wù)/請求。
分析工具使用
使用工具分析線程轉(zhuǎn)儲將提供有關(guān)線程及其狀態(tài)的許多統(tǒng)計信息。然而,有時它可能無法揭示系統(tǒng)中的真正瓶頸。手動處理線程轉(zhuǎn)儲并通過Notepad++等工具進(jìn)行分析總是更好的。如果要分析許多線程轉(zhuǎn)儲,則可以使用IBM線程轉(zhuǎn)儲分析器等工具。在有組織的視圖中查看線程轉(zhuǎn)儲有助于加快分析過程。雖然它不會像在線分析工具那樣提供許多復(fù)雜的統(tǒng)計數(shù)據(jù),但它可以幫助更好地可視化線程轉(zhuǎn)儲,提供一個視圖來查看由于另一個線程而被阻塞的線程,還可以幫助比較線程轉(zhuǎn)儲。
在分析線程轉(zhuǎn)儲時,了解線程轉(zhuǎn)儲是針對哪個應(yīng)用程序服務(wù)器進(jìn)行的非常重要,因為這將有助于集中精力分析正確的線程。例如,如果在WebSphere應(yīng)用程序服務(wù)器上進(jìn)行了線程轉(zhuǎn)儲,那么“Web容器”線程池應(yīng)該是第一個開始分析的地方,因為它是WebSphere應(yīng)用程序的入口點,它將開始為到達(dá)它的請求提供服務(wù)。
線程轉(zhuǎn)儲類型
通常,在線程轉(zhuǎn)儲中會有兩種類型的線程。一類線程與應(yīng)用程序相關(guān),有助于執(zhí)行應(yīng)用程序代碼。另一類是將執(zhí)行操作的線程,如從網(wǎng)絡(luò)讀取/寫入、心跳檢查和各種其他操作,包括JVM內(nèi)部(如GC)等。根據(jù)問題的不同,應(yīng)將重點放在這兩類線程上。大多數(shù)時候,應(yīng)用程序代碼可能是造成性能瓶頸的罪魁禍?zhǔn)?;因此,?yīng)該更多地關(guān)注應(yīng)用程序線程。
線程池
線程轉(zhuǎn)儲顯示應(yīng)用程序中可用的各種線程池。在WebSphere應(yīng)用程序服務(wù)器中,名為“WebContainer:<id>”的線程屬于WebSphere線程池。計算此類線程的數(shù)量應(yīng)等于定義的線程池大小。如果超過,則表示線程池中存在線程泄漏。需要驗證線程轉(zhuǎn)儲中不同線程池的大小。
ForkJoinPool是Java CompletableFuture用來異步運行任務(wù)的另一個線程池。如果此池中有太多異步任務(wù),則需要增加池的大小,或者需要創(chuàng)建另一個更大的池。否則,此ForkJoinPool將成為異步任務(wù)執(zhí)行的瓶頸。
如果應(yīng)用程序正在使用Java 執(zhí)行框架創(chuàng)建線程池,那么將為這些線程提供默認(rèn)名稱“pool-<id1>-thread-<id2>”。這里“id1”表示線程池編號,“id2”表示線程庫中的線程數(shù)。有時,如果開發(fā)人員每次都創(chuàng)建新的線程池,而沒有通過執(zhí)行框架關(guān)閉它們,那么它每次都會創(chuàng)建不同的池,線程數(shù)量也會增加。如果線程沒有主動執(zhí)行某件事,這可能不會造成問題,但會導(dǎo)致OutOfMemoryError,即無法通過達(dá)到線程創(chuàng)建的最大數(shù)量來創(chuàng)建新線程。在分析任何線程轉(zhuǎn)儲時,查看不同的線程池并確保所有線程池都在定義/預(yù)期的限制內(nèi)總是很好的。
應(yīng)用方法
關(guān)注線程轉(zhuǎn)儲的堆棧跟蹤中的應(yīng)用程序方法有助于分析應(yīng)用程序代碼中的問題。如果應(yīng)用程序中有同步的代碼或塊,那么應(yīng)用程序線程將等待獲取對象上的鎖,以進(jìn)入特定的代碼/塊執(zhí)行。這將是昂貴的,因為通過讓其他線程等待,只允許一個線程進(jìn)入代碼執(zhí)行。這種情況可以在線程轉(zhuǎn)儲中看到,在線程轉(zhuǎn)儲中,線程等待獲取對象的鎖。如果不需要,可以修改代碼以避免這種同步。
結(jié)論
線程轉(zhuǎn)儲包含關(guān)于虛擬機(jī)、JVM參數(shù)、內(nèi)存、GC相關(guān)信息、運行它的硬件等等。始終建議查看這些可能有助于分析的詳細(xì)信息。