Java內存泄漏分析系列之一:使用jstack定位線程堆棧信息
原文地址:http://www.javatang.com
前一段時間上線的系統升級之後,出現了嚴重的高CPU的問題,於是開始了一系列的優化處理之中,現在將這個過程做成一個系列的文章。
基本概念
在對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
-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內存泄漏分析系列之一:使用jstack定位線程堆棧信息