Java內存泄漏分析系列之一:使用jstack定位線程堆棧信息

原文地址:http://www.javatang.comhtml

前一段時間上線的系統升級以後,出現了嚴重的高CPU的問題,因而開始了一系列的優化處理之中,如今將這個過程作成一個系列的文章。java

基本概念shell

在對Java內存泄漏進行分析的時候,須要對jvm運行期間的內存佔用、線程執行等狀況進行記錄的dump文件,經常使用的主要有thread dump和heap dump。bash

  • thread dump 主要記錄JVM在某一時刻各個線程執行的狀況,以棧的形式顯示,是一個文本文件。經過對thread dump文件能夠分析出程序的問題出如今什麼地方,從而定位具體的代碼而後進行修正。thread dump須要結合佔用系統資源的線程id進行分析纔有意義。
  • heap dump 主要記錄了在某一時刻JVM堆中對象使用的狀況,即某個時刻JVM堆的快照,是一個二進制文件,主要用於分析哪些對象佔用了太對的堆空間,從而發現致使內存泄漏的對象。

上面兩種dump文件都具備實時性,所以須要在服務器出現問題的時候生成,而且多生成幾個文件,方便進行對比分析。下面咱們先來講一下如何生成 thread dump。服務器

使用jstack生成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文件

上面講述了整個的分析過程,不過全部的命令就是實時的,因此最好建立一個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生成的文件進行分析

相關文章
相關標籤/搜索