原文地址:http://www.javatang.comhtml
前一段時間上線的系統升級以後,出現了嚴重的高CPU的問題,因而開始了一系列的優化處理之中,如今將這個過程作成一個系列的文章。java
基本概念shell
在對Java內存泄漏進行分析的時候,須要對jvm運行期間的內存佔用、線程執行等狀況進行記錄的dump文件,經常使用的主要有thread dump和heap dump。bash
上面兩種dump文件都具備實時性,所以須要在服務器出現問題的時候生成,而且多生成幾個文件,方便進行對比分析。下面咱們先來講一下如何生成 thread dump。服務器
當服務器出現高CPU的時候,首先執行 top -c
命令動態顯示進程及佔用資源的排行,以下圖:
top後面的參數-c
能夠顯示進程詳細的信息。top
命令執行的時候還能夠執行一些快捷鍵:oracle
1
對於多核服務器,能夠顯示各個CPU佔用資源的狀況shift+h
顯示全部的線程信息shift+w
將當前 top
命令的設置保存到 ~/.toprc
文件中,這樣不用每次都執行快捷鍵了以上圖爲例,pid爲1503的進程佔用了大量的CPU資源,接下來須要將佔用CPU最高進程中的線程打印出來,能夠用 top -bn1 -H -p <pid>
命令,執行結果以下:
上面 -bn1
參數的含義是隻輸出一次結果,而不是顯示一個動態的結果。框架
我我的請喜歡用 ps -mp <pid> -o THREAD,tid,time | sort -k2r
命令查看,後面的sort參數根據線程佔用的cpu比例進行排序,結果以下:jvm
接下來咱們清楚今天的主角 jstack
,這是一個在JDK5開始提供的內置工具,能夠打印指定進程中線程運行的狀態,包括線程數量、是否存在死鎖、資源競爭狀況和線程的狀態等等。有下面的幾個經常使用的參數:工具
-l
長列表,打印關於鎖的附加信息-m
打印java和jni框架的全部棧信息由於thread id在棧信息中是以十六進制的形式顯示的,所以須要使用 printf "%x \n" <tid>
命令將現場id轉成十六進制的值,而後執行 jstack -l <pid> | grep <thread-hex-id> -A 10
命令顯示出錯的堆棧信息,以下圖:
上面命令中 -A 10
參數用來指定顯示行數,不然只會顯示一行信息。優化
這樣經過上圖,能夠很快地定位到程序問題的代碼,而後對代碼進行分析和改進便可。注意:須要在多個時間段提出多個 Thread Dump信息,而後綜合進行對比分析,單獨分析一個文件是沒有意義的。
上面講述了整個的分析過程,不過全部的命令就是實時的,因此最好建立一個shell腳本瞬間執行完成,下面對 當CPU飆高時,它在作什麼 這篇文章中所提供的shell進行了改進以下:
#!/bin/bash if [ $# -ne 1 ]; then echo "usage: $0 <pid> [line-number]" exit 1 fi # java home if test -z $JAVA_HOME then JAVA_HOME='/usr/local/jdk' fi #pid pid=$1 # checking pid if test -z "$($JAVA_HOME/bin/jps -l | cut -d '' -f 1 | grep $pid)" then echo "process of $pid is not exists" exit fi #line number if test -z $linenum then linenum=10 fi stackfile=stack$pid.dump threadsfile=threads$pid.dump # generate java stack $JAVA_HOME/bin/jstack -l $pid >> $stackfile ps -mp $pid -o THREAD,tid,time | sort -k2r | awk '{if ($1 !="USER" && $2 != "0.0" && $8 !="-") print $8;}' | xargs printf "%x\n" >> $threadsfile tids="$(cat $threadsfile)" for tid in $tids do echo "------------------------------ ThreadId ($tid) ------------------------------" cat $stackfile | grep 0x$tid -A $linenum done rm -f $stackfile $threadsfile
下一篇文章將要講述如何對jstack生成的文件進行分析