分析系列之一:使用jstack定位執行緒堆疊資訊
基本概念
在對Java記憶體洩漏進行分析的時候,需要對jvm執行期間的記憶體佔用、執行緒執行等情況進行記錄的dump檔案,常用的主要有thread dump和heap dump。
- thread dump 主要記錄JVM在某一時刻各個執行緒執行的情況,以棧的形式顯示,是一個文字檔案。通過對thread dump檔案可以分析出程式的問題出現在什麼地方,從而定位具體的程式碼然後進行修正。thread dump需要結合佔用系統資源的執行緒id進行分析才有意義。
- heap dump 主要記錄了在某一時刻JVM堆中物件使用的情況,即某個時刻JVM堆的快照,是一個二進位制檔案,主要用於分析哪些物件佔用了太對的堆空間,從而發現導致記憶體洩漏的物件。
上面兩種dump檔案都具有實時性,因此需要在伺服器出現問題的時候生成,並且多生成幾個檔案,方便進行對比分析。下面我們先來說一下如何生成 thread dump。
使用jstack生成thread dump
當伺服器出現高CPU的時候,首先執行 top -c
命令動態顯示程序及佔用資源的排行,如下圖:
top後面的引數-c
可以顯示程序詳細的資訊。top
命令執行的時候還可以執行一些快捷鍵:
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比例進行排序,結果如下:
接下來我們清楚今天的主角 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檔案
上面講述了整個的分析過程,不過所有的命令就是實時的,所以最好建立一個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生成的檔案進行分析
參考資料:
java程式效能分析之thread dump和heap dump
JVM調優之jstack找出最耗cpu的執行緒並定位程式碼
當CPU飆高時,它在做什麼
JAVA應用CPU佔用100%|記憶體洩漏分析總結