給一個系統定位問題的時候,知識、經驗是關鍵基礎,資料是依據,工具是運用知識處理資料的手段
資料 運作日志、異常堆棧、GC日志、線程快照(threaddump/javacore檔案)、堆轉儲快照(heapdump/hprof檔案)等。
JDK指令行 | 位置bin下的程式 |
---|---|
特點 | 27KB 是jdk/lib/tools.jar的封裝,核心實作再tools.jar中,tool.jar隻支援hotpot |
jps | JVM Process Status Tool 顯示指定系統内所有的HotSpot虛拟機程序 |
jstat | JVM statistics Monitoring Tool 用于收集HotSpot虛拟機各方面的運作資料 |
jinfo | Configuration Info for jva 顯示虛拟機配置資訊 |
jmap | Memory Map for java 生成虛拟機的記憶體轉儲快照(headdump檔案) |
jhat | JVM Heap Dump Browser 用于分析headdump檔案,它會建立一個HTTP/HTML伺服器,讓使用者可以在浏覽器上檢視分析結果 |
jstack | Stack Trace for java 顯示虛拟機的線程快照 |
- JPS
除了名字像UNIX的ps指令之外,它的功能也和ps指令類似:可以列出正在運作的虛拟機程序,并顯示虛拟機執行主類(Main Class,main()函數所在的類)名稱以及這些程序的本地虛拟機唯一ID(Local Virtual Machine Identifier,LVMID)
指令格式
jps[options][hostid]
![]()
JVM(四):虛拟機性能監控與故障處理工具
- jstat:虛拟機統計資訊監視工具
指令格式
jstat[option vmid[interval[s|ms][count]]]
eg
參數interval和count代表查詢間隔和次數,如果省略這兩個參數,說明隻查詢一次。假設需要每250毫秒查詢一次程序2764垃圾收集狀況,一共查詢20次
jstat -gc 2764 250 20
![]()
JVM(四):虛拟機性能監控與故障處理工具
執行樣例
這台伺服器的新生代Eden區(E,表示Eden)使用了6.2%的空間,兩個Survivor區(S0、S1,表示Survivor0、Survivor1)裡面都是空的,老年代(O,表示Old)和永久代(P,表示Permanent)則分别使用了41.42%和47.20%的空間。程式運作以來共發生Minor GC(YGC,表示Young GC)16次,總耗時0.105秒,發生Full GC(FGC,表示Full GC)3次,Full GC總耗時(FGCT,表示Full GC Time)為0.472秒,所有GC總耗時(GCT,表示GC Time)為0.577秒
- jmap
指令用于生成堆轉儲快照(一般稱為heapdump或dump檔案)。jmap的作用并不僅僅是為了擷取dump檔案,它還可以查詢finalize執行隊列、Java堆和永久代的詳細資訊,如空間使用率、目前用的是哪種收集器等。
指令格式
jmap[option]vmid
![]()
JVM(四):虛拟機性能監控與故障處理工具 ![]()
JVM(四):虛拟機性能監控與故障處理工具
- jhat 虛拟機堆轉儲快照分析工具
Sun JDK提供jhat(JVM Heap Analysis Tool)指令與jmap搭配使用,來分析jmap生成的堆轉儲快照。jhat内置了一個微型的HTTP/HTML伺服器,生成dump檔案的分析結果後,可以在浏覽器中檢視。不過實事求是地說,在實際工作中,除非筆者手上真的沒有别的工具可用,否則一般都不會去直接使用jhat指令來分析dump檔案,主要原因有二:一是一般不會在部署應用程式的伺服器上直接分析dump檔案,即使可以這樣做,也會盡量将dump檔案複制到其他機器[1]上進行分析,因為分析工作是一個耗時而且消耗硬體資源的過程,既然都要在其他機器進行,就沒有必要受到指令行工具的限制了;另一個原因是jhat的分析功能相對來說比較簡陋,後文将會介紹到的VisualVM,以及專業用于分析dump檔案的Eclipse Memory Analyzer、IBM HeapAnalyzer[2]等工具,都能實作比jhat更強大更專業的分析功能。代碼清單[…]螢幕顯示"Server is ready.“的提示後,使用者在浏覽器中鍵入http://localhost:7000/就可以看到分析結果,如圖4-3所示。![]()
JVM(四):虛拟機性能監控與故障處理工具 分析結果預設是以包為機關進行分組顯示,分析記憶體洩漏問題主要會使用到其中的"Heap Histogram”(與jmap-histo功能一樣)與OQL頁簽的功能,前者可以找到記憶體中總容量最大的對象,後者是标準的對象查詢語言,使用類似SQL的文法對記憶體中的對象進行查詢統計,讀者若對OQL有興趣的話,可以參考本書附錄D的介紹。![]()
JVM(四):虛拟機性能監控與故障處理工具
- jstack
jstack(Stack Trace for Java)指令用于生成虛拟機目前時刻的線程快照(一般稱為threaddump或者javacore檔案)。線程快照就是目前虛拟機内每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導緻的長時間等待等都是導緻線程長時間停頓的常見原因。線程出現停頓的時候通過jstack來檢視各個線程的調用堆棧,就可以知道沒有響應的線程到底在背景做些什麼事情,或者等待着什麼資源
jstack指令格式:
jstack[option]vmid
![]()
JVM(四):虛拟機性能監控與故障處理工具 eg:
使用jstack檢視Eclipse線程堆棧的例子,例子中的3500是通過jps指令查詢到的LVMID
![]()
JVM(四):虛拟機性能監控與故障處理工具
在JDK 1.5中,java.lang.Thread類新增了一個getAllStackTraces()方法用于擷取虛拟機中所有線程的StackTraceElement對象。使用這個方法可以通過簡單的幾行代碼就完成jstack的大部分功能,在實際項目中不妨調用這個方法做個管理者頁面,可以随時使用浏覽器來檢視線程堆棧,
<%@page import="java.util.Map"%>
<html>
<head>
<title>伺服器線程資訊</title>
</head>
<body>
<pre>
<%
for(Map.Entry<Thread,StackTraceElement[]>stackTrace:Thread.
getAllStackTraces().entrySet()){
Thread thread=(Thread)stackTrace.getKey();
StackTraceElement[]stack=(StackTraceElement[])stackTrace.getValue();
if(thread.equals(Thread.currentThread())){
continue;
}
out.print("\n線程:"+thread.getName()+"\n");
for(StackTraceElement element:stack){
out.print("\t"+element+"\n");
}
}
%>
</pre>
</body>
</html>”
-
HSDIS JIT生成代碼反彙編
JIT 編譯器動态生成
分析程式如何執行,通過軟體調試工具(GDB、Windbg等)來斷點調試是最常見的手段,但是這樣的調試方式在Java虛拟機中會遇到很大困難,因為大量執行代碼是通過JIT編譯器動态生成到CodeBuffer中的,沒有很簡單的手段來處理這種混合模式的調試(不過相信虛拟機開發團隊内部肯定是有内部工具的)。是以,不得不通過一些特别的手段來解決問題,基于這種背景,本節的主角——HSDIS插件就正式登場。
備注對列印的資訊看不懂,需要更細的學習,但這次不需要深入,了解即可
##JDK的可視化工具
JDK可視工具 | |
---|---|
JConsole | * |
VisualVM | * |
###JConsole
JConsole(Java Monitoring and Management Console)是一種基于JMX的可視化監視、管理工具。它管理部分的功能是針對JMX MBean進行管理,由于MBean可以使用代碼、中間件伺服器的管理控制台或者所有符合JMX規範的軟體進行通路,是以本節将會着重介紹JConsole監視部分的功能。
1. 啟動JConsole
通過JDK/bin目錄下的"jconsole.exe"啟動JConsole後,将自動搜尋出本機運作的所有虛拟機程序,不需要使用者自己再使用jps來查詢了,如圖4-4所示。輕按兩下選擇其中一個程序即可開始監控,也可以使用下面的“遠端程序”功能來連接配接遠端伺服器,對遠端虛拟機進行監控。
機器現在運作了Eclipse、JConsole和MonitoringTest三個本地虛拟機程序,其中MonitoringTest就是筆者準備的“反面教材”代碼之一。輕按兩下它進入JConsole主界面,可以看到主界面裡共包括“概述”、“記憶體”、“線程”、“類”、“VM摘要”、"MBean"6個頁簽
2. 記憶體監控
記憶體”頁簽相當于可視化的jstat指令,用于監視受收集器管理的虛拟機記憶體(Java堆和永久代)的變化趨勢。我們通過運作代碼清單4-8中的代碼來體驗一下它的監視功能。運作時設定的虛拟機參數為:-Xms100m-Xmx100m-XX:+UseSerialGC,這段代碼的作用是以64KB/50毫秒的速度往Java堆中填充資料,一共填充1000次,使用JConsole的“記憶體”頁簽進行監視,觀察曲線和柱狀訓示圖的變化。
3. 線程監控
如果上面的“記憶體”頁簽相當于可視化的jstat指令的話,“線程”頁簽的功能相當于可視化的jstack指令,遇到線程停頓時可以使用這個頁簽進行監控分析。前面講解jstack指令的時候提到過線程長時間停頓的主要原因主要有:等待外部資源(資料庫連接配接、網絡資源、裝置資源等)、死循環、鎖等待(活鎖和死鎖)
VisualVM:多合一故障處理工具
VisualVM(All-in-One Java Troubleshooting Tool)是到目前為止随JDK釋出的功能最強大的運作監視和故障處理程式,并且可以預見在未來一段時間内都是官方主力發展的虛拟機故障處理工具。官方在VisualVM的軟體說明中寫上了"All-in-One"的描述字樣,預示着它除了運作監視、故障處理外,還提供了很多其他方面的功能。如性能分析(Profiling),VisualVM的性能分析功能甚至比起JProfiler、YourKit等專業且收費的Profiling工具都不會遜色多少,而且VisualVM的還有一個很大的優點:不需要被監視的程式基于特殊Agent運作,是以它對應用程式的實際性能的影響很小,使得它可以直接應用在生産環境中。這個優點是JProfiler、YourKit等工具無法與之媲美的。
1.VisualVM相容範圍與插件安裝”
VisualVM基于NetBeans平台開發,是以它一開始就具備了插件擴充功能的特性,通過插件擴充支援,VisualVM可以做到:
- 監視應用程式的CPU、GC、堆、方法區以及線程的資訊(jstat、jstack)。
- dump以及分析堆轉儲快照(jmap、jhat)。
- 方法級的程式運作性能分析,找出被調用最多、運作時間最長的方法。
-
離線程式快照:收集程式的運作時配置、線程dump、記憶體dump等資訊建立一個快照,可以将快照發送開發者處進行Bug回報。
其他plugins的無限的可能性……
JVM(四):虛拟機性能監控與故障處理工具
首次啟動VisualVM後,讀者先不必着急找應用程式進行監測,因為現在VisualVM還沒有加載任何插件,雖然基本的監視、線程面闆的功能主程式都以預設插件的形式提供了,但是不給VisualVM裝任何擴充插件,就相當于放棄了它最精華的功能,和沒有安裝任何應用軟體作業系統差不多。
插件可以進行手工安裝,在相關網站[2]上下載下傳*.nbm包後,點選“工具”→“插件”→“已下載下傳”菜單,然後在彈出的對話框中指定nbm包路徑便可進行安裝,插件安裝後存放在JDK_HOME/lib/visualvm/visualvm中。不過手工安裝并不常用,使用VisualVM的自動安裝功能已經可以找到大多數所需的插件,在有網絡連接配接的環境下,點選“工具”→“插件菜單”,彈出如圖4-11所示的插件頁簽,在頁簽的“可用插件”中列舉了目前版本VisualVM可以使用的插件,選中插件後在右邊視窗将顯示這個插件的基本資訊,如開發者、版本、功能描述等。
![]()
JVM(四):虛拟機性能監控與故障處理工具
大家可以根據自己的工作需要和興趣選擇合适的插件,然後點選安裝按鈕,彈出如圖4-12所示的下載下傳進度視窗,跟着提示操作即可完成安裝。
安裝完插件,選擇一個需要監視的程式就進入程式的主界面了,如圖4-13所示。根據讀者選擇安裝插件數量的不同,看到的頁簽可能和圖4-13中的有所不同。
VisualVM中“概述”、“監視”、“線程”、"MBeans"的功能與前面介紹的JConsole差别不大,讀者根據上文内容類比使用即可,下面挑選幾個特色功能、插件進行介紹
2.生成、浏覽堆轉儲快
“在VisualVM中生成dump檔案有兩種方式,可以執行下列任一操作:
在“應用程式”視窗中右鍵單擊應用程式節點,然後選擇“堆Dump”。
在“應用程式”視窗中輕按兩下應用程式節點以打開應用程式标簽,然後在“監視”标簽中單擊“堆Dump”。
生成了dump檔案之後,應用程式頁簽将在該堆的應用程式下增加一個以[heapdump]開頭的子節點,并且在首頁簽中打開了該轉儲快照,如圖4-14所示。如果需要把dump檔案儲存或發送出去,要在heapdump節點上右鍵選擇“另存為”菜單,否則當VisualVM關閉時,生成的dump檔案會被當做臨時檔案删除掉。要打開一個已經存在的dump檔案,通過檔案菜單中的“裝入”功能,選擇硬碟上的dump檔案即可。
從堆頁簽中的“摘要”面闆可以看到應用程式dump時的運作時參數、System.getProperties()的内容、線程堆棧等資訊,“類”面闆則是以類為統計口徑統計類的執行個體數量、容量資訊,“執行個體”面闆不能直接使用,因為不能确定使用者想檢視哪個類的執行個體,是以需要通過“類”面闆進入,在“類”中選擇一個關心的類後輕按兩下滑鼠,即可在“執行個體”裡面看見此類中500個執行個體的具體屬性資訊。“OQL控制台”面闆中就是運作OQL查詢語句的,同jhat中介紹的OQL功能一樣。如果需要了解具體OQL文法和使用,可參見本書附錄D的内容。
3.分析程式性能
在Profiler頁簽中,VisualVM提供了程式運作期間方法級的CPU執行時間分析以及記憶體分析,做Profiling分析肯定會對程式運作性能有比較大的影響,是以一般不在生産環境中使用這項功能。
要開始分析,先選擇"CPU"和“記憶體”按鈕中的一個,然後切換到應用程式中對程式進行操作,VisualVM會記錄到這段時間中應用程式執行過的方法。如果是CPU分析,将會統計每個方法的執行次數、執行耗時;如果是記憶體分析,則會統計每個方法關聯的對象數以及這些對象所占的空間。分析結束後,點選“停止”按鈕結束監控過程,如圖4-15所示。
注意 在JDK 1.5之後,在Client模式下的虛拟機加入并且自動開啟了類共享——這是一個在多虛拟機程序中共享rt.jar中類資料以提高加載速度和節省記憶體的優化,而根據相關Bug報告的反映,VisualVM的Profiler功能可能會因為類共享而導緻被監視的應用程式崩潰,是以讀者進行Profiling前,最好在被監視程式中使用-Xshare:off參數來關閉類共享優化。
圖4-15中是對Eclipse IDE一段操作的錄制和分析結果,讀者分析自己的應用程式時,可以根據實際業務的複雜程度與方法的時間、調用次數做比較,找到最有優化價值的方法。
4.BTrace動态日志跟蹤
BTrace[3]是一個很“有趣”的VisualVM插件,本身也是可以獨立運作的程式。它的作用是在不停止目标程式運作的前提下,通過HotSpot虛拟機的HotSwap技術[4]動态加入原本并不存在的調試代碼。這項功能對實際生産中的程式很有意義:經常遇到程式出現問題,但排查錯誤的一些必要資訊,譬如方法參數、傳回值等,在開發時并沒有列印到日志之中,以至于不得不停掉服務,通過調試增量來加入日志代碼以解決問題。當遇到生産環境服務無法随便停止時,缺一兩句日志導緻排錯進行不下去是一件非常郁悶的事情。
在VisualVM中安裝了BTrace插件後,在應用程式面闆中右鍵點選要調試的程式,會出現"Trace Application……"菜單,點選将進入BTrace面闆。這個面闆裡面看起來就像一個簡單的Java程式開發環境,裡面還有一小段Java代碼,如圖4-16所示。