JVM 除錯工具入門
本文由碼農網 – 聶嘯原創,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!
筆者上週末連續兩天凌晨都收到了系統的記憶體使用率過高報警,在分析監控系統記錄的記憶體使用率曲線和記憶體使用情況後發現,主要是因為在老年代遲遲沒有觸發full gc導致監控系統連續多次監測到可用記憶體過低,而觸發的報警。在系統觸發一次full gc之後,記憶體使用率會顯著下降,報警也沒有持續下去。由於無法復現問題,具體原因仍未找到,但是通過此過程,學習到的記憶體分析工具與方法,卻值得記錄一番。
jstat
jstat是HotSpot Java虛擬機器的效能統計工具。其基本的語法描述如下:
jstat [generalOption | outputOptions | vmid [interval[s|ms] [count]]]
generalOption
generalOption是針對jstat功能的描述,包括兩個引數 -help 與 -options ,分別用於提示jstat的用法和支援的統計選項。該選項具有排他性,只能單獨使用。
outputOptions
outputOptions包括兩類引數:狀態統計 和 格式化輸出。狀態統計引數用於指定jstat命令希望獲取虛擬機器哪方面的資訊,而格式化引數則用於控制命令輸出的展示樣式。
jstat支援的狀態統計引數(即jstat -options的輸出)及功能描述如下:
-class 統計類載入行為
-complier 統計HotSpot即時編譯器的行為
-gc 統計關於堆記憶體垃圾回收的行為
-gccapacity 統計堆記憶體中各分割槽的使用情況
-gccause 垃圾回收行為彙總,比-gcutil多輸出最近兩次垃圾回收的原因
-gcnew,-gcold 新生代,老年代行為資訊(記憶體量,閾值,垃圾回收次數等)
-gcnewcapacity 新生代記憶體容量和使用量資訊
-gcoldcapacity 老年代記憶體容量和使用量資訊
-gcpermcapacity 持久區記憶體容量和使用量資訊
-gcutil 垃圾回收行為彙總
-printcompilation HotSpot編譯方法統計
格式化引數包括三個:-h n ,-t 和 -JjavaOption. -h引數指定每隔n行重新顯示一次列名;-t引數控制在輸出第一列新增時間戳資訊;-JjavaOption用於傳遞javaOption到java程式啟動引數。比如,-J-Xms48m 設定java啟動最小記憶體為48M。
vmid
vmid是待監測的目標java程式識別符號,可用 jps 和Linux系統下的 ps 等操作獲取。vmid引數也支援以URI形式指定的遠端主機上執行的java程式,不常用,不再贅述。
interval and count
這兩個引數用於控制jstat命令監測並輸出的頻率,interval預設引數為毫秒,如果設定了該引數,jstat命令將每隔interval的時間輸出一次,count控制jstat命令輸出樣例的個數,也就是輸出的行數。如果不設定,預設為無限,jstat會一直進行輸出直到目標程式退出或者jstat命令終止。
樣例
使用 -gcutil 引數檢視程式16058發生的垃圾回收行為,每2秒列印一次結果,一共列印5次。命令輸出的每一列都使用簡稱的形式展示,下圖中 S0 表示Survivor 0區的空間使用比例, E, O, P 分別代表Eden, Old和Perm空間使用率,YGC 表示young gc的次數,YGCT 表示young gc消耗的時間。GCT 則用來統計執行gc的總時間。
使用 -gc 引數檢視更詳細的垃圾回收與堆記憶體資訊。 S0 以及 E 等各記憶體區域標識字尾中 C 表示 Capacity, U 表示Utilization,引數對應的數值顯示的是真實容量,而不是百分比。
使用 -gcnew 引數檢視新生代的記憶體和垃圾回收情況,由於命令附帶了引數 t ,所以第一列列印了時間戳的資訊。 TT 參數列示物件在gc時被放入老年代的年齡期限閾值,MTT 參數列示最大閾值. 這兩個引數之間 MTT 設定了 TT 可取的最大值,TT 實際控制著物件進入老年代的年齡限制,會隨著垃圾回收過程而發生變化。
當年齡從1開始的物件大小累計超過了Survivor區域的1/2(TargetSurvivorRatio所定義)時,會計算一個Thenuring Threshold,超過這個年齡的新生代物件會進入到老年代,即使這時候新生代還有很多的空間。注意MaxTenuringThreshold只是設定了最大的Thenuring Threshold,不是說只有大於Max Tenuring Threshold才會進入到老年代,而是隻要超過了計算出來的Tenuring Threshold就會進入老年代,MaxTenuringThreshold規定了Tenuring Threshold的最大值而已。Tenuring Threshold這個值在每一輪GC後都會動態計算,它與TargetSurvivorRatio以及Survivor區的大小有關係,TargetSurivivor預設是50即Survivor的1/2, 會計算出一個Desired Survivor Size,當年齡從1開始的物件大小累計超過了這個Desired Survivor Size,那麼這個age就是Tenuring Threshold的值
使用 -gcnewcapacity 引數檢視新生代的記憶體佔用情況。NGC : new generation capacity, 新生代記憶體大小。NGCMN 表示新生代分配記憶體的最小值,NGCMX 新生代分配記憶體的最大值,NGCMX=S0CMX+S1CMX+ECMX.
jstack
jstack用於列印指定java程式或者核心檔案中所有java執行緒當前時刻正在執行的方法堆疊追蹤情況,也就是執行緒的snapshot。生成執行緒的快照主要用於定位執行緒長時間出現停頓的原因:如死鎖,等待外部資源等。jstack的命令格式如下:
jstack [option] pid/executable core/[server-id@]remote-hostname-or-IP
- pid
待追蹤的java程式ID,可使用jps等方式獲得
- executable
產生core dump的java可執行程式(jar檔案)
- core
列印棧追蹤資訊的核心檔案
options
-F 當正常輸出的請求(jstack [-l] pid)不被響應時,強制執行stack dump
-l 除了堆疊外,列印關於鎖的附加資訊
-m 如果呼叫本地方法,同時列印java和本地C/C++棧幀
-h 列印幫助
jstack在分析死鎖,阻塞等效能問題上非常有用,根據列印的堆疊資訊可以定位到出問題的程式碼段。定位問題的思路根據要解決的問題而發生不同,比如可以首先找到java程式中最耗cpu的執行緒,再根據執行緒id在jstack的輸出中定位,或者使用指定的執行緒名稱定位。下面看一下jstack的輸出格式:
main 執行緒名稱
prio=5 執行緒優先順序為5
tid=7f8a7c001800 jvm中執行緒識別符號
nid=0x700000a19000 16進製表示的本地執行緒識別符號
runnnable 執行緒狀態
[70000a17000] 執行緒的起始地址
從第二行起為函式呼叫棧
上述資訊中最重要的狀態莫過於執行緒的狀態,當程式發生問題時往往能夠據此幫我們找到問題的所在。在jstack的輸出中,執行緒所處的狀態包括:
- Runnable: 正在執行
- Wait on condition: 該狀態出現線上程等待某個條件的發生。具體是什麼原因,可以結合 stacktrace來分析。最常見的情況是執行緒在等待網路的讀寫。如果網路資料沒準備好,執行緒就等待在那裡。另外一種出現 Wait on condition的常見情況是該執行緒在 sleep,等待 sleep的時間到了時候,將被喚醒。
- Wait for monitor entry: Java通過物件監視器來進行執行緒的互斥與同步的,每個物件都有一個物件監視器,在對物件加鎖的過程中,任何時刻只有一個執行緒擁有這個物件監視器,其他請求獲得此資源的執行緒將會被分為兩種:線上程獲取到物件監視器,但等待的資源仍未到達時,執行緒可能呼叫Object.wait(),釋放鎖並進入Object.wait()的狀態,重新等待;而那些從未獲得過此物件監視器的執行緒就將被標識為Wait for monitor entry的狀態。
- Object.wait: 等待物件監視器(鎖)的狀態。
關於兩種等待狀態可以參考下面的圖(來源於網路):
jmap
jmap用於生成java程式的heapdump或者堆記憶體的詳細資訊。可以用來分析java程式堆記憶體被各種例項佔據的比例或者GC回收了哪些物件等資訊。jmap的命令格式與jstack一致,不再贅述。
options
- <no option>
列印每一個共享物件的起始地址,範圍等資訊
-dump:\[live,]format=b,file=<filename>
以二進位制形式列印java堆的dump資訊到指定檔案中,指定live引數,只列印存活的物件
-heap
顯示java堆的詳細資訊:GC演算法,堆配置以及分代情況
-histo\[:live]
顯示堆中每一個java類例項的個數,佔據空間的大小,類名全稱等。live引數控制輸出存活物件。
-premstat
以classLoader為統計入口,顯示永久代的生存狀態。每個載入器的名稱,存活狀態,地址,父載入器和已經載入類的數量等資訊將被列印。
-F
在正常的命令對-dump或者-histo沒有響應時,強制執行,生成dump資訊
-J<flag>
map啟動時傳遞給jvm的引數,比如在64位機器上就要使用jmap -J-d64 -heap pid來執行命令。
參考連結
1.http://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html
2.http://docs.oracle.com/javase/7/docs/technotes/tools/share/jstack.html
3.http://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html
4.https://my.oschina.net/feichexia/blog/196575
5.http://go-on.iteye.com/blog/1673894
6.http://blog.csdn.net/iter_zc/article/details/41802365
7.《深入理解JVM虛擬機器》周志明著
本文連結:http://www.codeceo.com/article/jvm-debug-tools-guide.html
本文作者:碼農網 – 聶嘯
[ 原創作品,轉載必須在正文中標註並保留原文連結和作者等資訊。]
相關文章
- Node 除錯工具入門教程除錯
- 前端除錯入門前端除錯
- Kafka除錯入門(一)Kafka除錯
- DebugView 除錯入門View除錯
- .NET高階除錯系列-Windbg除錯入門篇高階除錯
- JVM(一)-JVM入門JVM
- Google Chrome除錯js入門GoChrome除錯JS
- JVM入門JVM
- 想寫一篇jvm的工具入門JVM
- Qt入門(10)——除錯技術QT除錯
- JVM高階效能除錯JVM除錯
- JVM中jhsdb除錯教程JVM除錯
- GDB除錯-從入門到實踐除錯
- NodeMCU入門:燒錄、除錯、聯網除錯
- 程式碼除錯-入門、實踐到原理除錯
- Xcode Instruments除錯swift入門教程XCode除錯Swift
- 前端入門技巧之瀏覽器除錯前端瀏覽器除錯
- JVM入門--類載入器JVM
- ios 除錯工具iOS除錯
- JVM高階效能除錯實戰JVM除錯
- Mqtt入門:線上除錯連線阿里雲MQQT除錯阿里
- JVM-垃圾收集入門JVM
- 【死磕JVM】JVM快速入門之前戲篇JVM
- JVM效能優化(一)JVM技術入門JVM優化
- 求golang除錯工具?Golang除錯
- 效能除錯工具——oprofile除錯
- gdb 除錯入門,大牛寫的高質量指南除錯
- JVM(一)史上最佳入門指南JVM
- JVM垃圾回收機制入門JVM
- JVM 從入門到實戰--- 01 JVM 基本介紹JVM
- 使用 gdb 工具除錯 Go除錯Go
- Windows 除錯工具課程Windows除錯
- JVM(五)-垃圾收集器入門JVM
- 「入門篇」初識JVM (下下) - GCJVMGC
- JVM入門(JVM引數詳解和常用命令)JVM
- Linux下的除錯工具Linux除錯
- serial for mac 串列埠除錯工具Mac串列埠除錯
- Go 語言 udpproxy 除錯工具GoUDP除錯