linux下保證高可用Java服務化系統高效運行的必備工具腳本

無論是技術開發人員還是架構設計人員都是在實踐中成長起來的,他們通過實踐進行總結,總結後把經驗昇華並再次應用到實踐中去,進一步提供生產效率。

本文介紹筆者在互聯網公司裏線上應急和技術攻關過程中積累的應用層腳本和Java虛擬機命令,這些腳本和命令在發現問題和定位問題的過程中起到關鍵作用,在特定的問題環境下,堪稱快速定位問題的小倚天劍以及快速解決問題的微屠龍刀。

本文在介紹腳本和命令之前,先給大家介紹筆者的Linux環境以及在Linux環境下搭建的一個原創Java發號器服務,用來向大家演示腳本和命令的使用方法,力爭做到讓大家拿來即用的效果。另外,在介紹完所有的腳本和命令之後,我會把所有的命令和腳本收集在一個表格中,便於大家隨時參考和使用,並推薦大家把這個表格打印出來放在自己的辦公桌上,需要的時候看一眼,便可快速發現和解決問題的工具。

腳本和命令系列主題中計劃提供兩篇文章,這篇文章是腳本和命令系列主題中的其中一篇,本文聚焦在那些“神奇的”應用層腳本和Java虛擬機命令,曾經在不同程度上幫助筆者在線上應急和技術攻關的過程中解決過不小的問題,通過這篇文章把這些腳本和命令推廣給讀者,讓讀者也能夠應用在實踐中,切實有效的幫助讀者解決實際問題。

環境搭建和示例服務啓動

首先,使用的Linux版本爲:

OS:Ubuntu 14.04.2 LTS

內核:3.16.0-30-generic

硬件架構:x86_64

使用的JDK版本爲:

jdk1.8.0_20

爲了讓大家瞭解使用命令的真實環境,我們在Linux系統中搭建了原創發號器服務Vesta,在介紹下面的每個命令的同時,都會以監控和維護原創發號器服務爲例來說明,如果大家對發號器服務的設計、實現和使用方式感興趣,或者大家想了解如何開發一款專業的開源軟件,請參考文章如何設計一款多場景分佈式發號器,本文聚焦在如何使用這些高效的腳本和Linux命令來維護一款線上服務。

爲了搭建Vesta發號器服務,我們需要從Github地址https://github.com/robertleepeak/vesta-id-generator.git下載源代碼:

robert@robert-ubuntu1410:~/working/workspace$ git clone https://github.com/robertleepeak/vesta-id-generator.git
正克隆到 'vesta-id-generator'...
remote: Counting objects: 944, done.
remote: Compressing objects: 100% (53/53), done.
接收對象中: 100% (944/944), 474.66 KiB | 337.00 KiB/s, done.
remote: Total 944 (delta 19), reused 0 (delta 0), pack-reused 881
處理 delta 中: 100% (221/221), done.
檢查連接... 完成。

下載後工作目錄爲:

robert@robert-ubuntu1410:~/working/workspace$ ll
總用量 12
drwxrwxr-x  3 robert robert 4096  4月  8 18:31 ./
drwxr-xr-x  6 robert robert 4096  4月  8 17:33 ../
drwxrwxr-x 12 robert robert 4096  4月  8 18:31 vesta-id-generator/

進入Vesta發號器項目根目錄並列出目錄結構:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ ll
總用量 108
drwxrwxr-x 12 robert robert  4096  4月  8 18:31 ./
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 ../
-rwxrwxr-x  1 robert robert  3843  4月  8 18:31 assembly.xml*
-rwxrwxr-x  1 robert robert   494  4月  8 18:31 brief.txt*
-rwxrwxr-x  1 robert robert    65  4月  8 18:31 deploy-maven.sh*
-rw-rw-r--  1 robert robert 12292  4月  8 18:31 .DS_Store
drwxrwxr-x  8 robert robert  4096  4月  8 18:31 .git/
-rwxrwxr-x  1 robert robert   231  4月  8 18:31 .gitignore*
-rwxrwxr-x  1 robert robert 11358  4月  8 18:31 LICENSE*
-rwxrwxr-x  1 robert robert   902  4月  8 18:31 make-release.sh*
-rwxrwxr-x  1 robert robert  1985  4月  8 18:31 pom.xml*
-rwxrwxr-x  1 robert robert  2515  4月  8 18:31 README.md*
-rwxrwxr-x  1 robert robert  1429  4月  8 18:31 todo.txt*
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-client/
drwxrwxr-x  2 robert robert  4096  4月  8 18:31 vesta-doc/
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-intf/
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-rest/
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-rest-netty/
drwxrwxr-x  4 robert robert  4096  4月  8 18:31 vesta-sample/
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-server/
drwxrwxr-x  4 robert robert  4096  4月  8 18:31 vesta-service/
drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-theme/

然後,直接運行發佈腳本 make-release.sh 進行打包發佈:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ ./make-release.sh 
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] vesta-id-generator
[INFO] vesta-intf
[INFO] vesta-service
[INFO] vesta-rest
[INFO] vesta-rest-netty
[INFO] vesta-client
[INFO] vesta-server
[INFO] vesta-sample
[INFO] vesta-sample-embed
[INFO] vesta-sample-client
......
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] vesta-id-generator ................................. SUCCESS [ 11.630 s]
[INFO] vesta-intf ......................................... SUCCESS [  3.489 s]
[INFO] vesta-service ...................................... SUCCESS [  1.140 s]
[INFO] vesta-rest ......................................... SUCCESS [  4.749 s]
[INFO] vesta-rest-netty ................................... SUCCESS [  2.482 s]
[INFO] vesta-client ....................................... SUCCESS [  0.111 s]
[INFO] vesta-server ....................................... SUCCESS [  1.879 s]
[INFO] vesta-sample ....................................... SUCCESS [  0.005 s]
[INFO] vesta-sample-embed ................................. SUCCESS [  0.475 s]
[INFO] vesta-sample-client ................................ SUCCESS [  0.200 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.026 s
[INFO] Finished at: 2017-04-08T21:31:34+08:00
[INFO] Final Memory: 42M/616M
[INFO] ------------------------------------------------------------------------

最後,啓動發號器REST服務:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin$ ll
總用量 110968
drwxrwxr-x 2 robert robert     4096  4月  8 21:34 ./
drwxrwxr-x 6 robert robert     4096  4月  8 21:34 ../
-rw-rw-r-- 1 robert robert 47117221  4月  8 21:34 vesta-all-src-0.0.1.tar.gz
-rw-rw-r-- 1 robert robert 34524462  4月  8 21:34 vesta-lib-0.0.1.tar.gz
-rw-rw-r-- 1 robert robert 14745549  4月  8 21:33 vesta-rest-0.0.1-bin.tar.gz
-rw-rw-r-- 1 robert robert  8656473  4月  8 21:33 vesta-rest-netty-0.0.1-bin.tar.gz
-rw-rw-r-- 1 robert robert  8539966  4月  8 21:33 vesta-server-0.0.1-bin.tar.gz
-rw-rw-r-- 1 robert robert    27079  4月  8 21:34 vesta-src-0.0.1.tar.gz
robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin$ tar xzvf vesta-rest-0.0.1-bin.tar.gz
vesta-rest-0.0.1/bin/
vesta-rest-0.0.1/bin/server.sh
vesta-rest-0.0.1/bin/start.sh
vesta-rest-0.0.1/bin/check.sh
vesta-rest-0.0.1/bin/stop.sh
vesta-rest-0.0.1/lib/vesta-rest-0.0.1.jar
vesta-rest-0.0.1/lib/vesta-rest-0.0.1-sources.jar

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ ./start.sh
apppath: /home/robert/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1
Vesta Rest Server is started.

然後,使用curl語句測試發號器服務是否正常運行:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ curl "http://localhost:8080/genid"
2382742310220727293

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ curl "http://localhost:8080/expid?id=2382742310220727293"
{"machine":1021,"seq":0,"time":71618055,"genMethod":2,"type":0,"version":0}

看見發號器正確產生ID=2382742310220727293,並能反解成JSON字符串:

{"machine":1021,"seq":0,"time":71618055,"genMethod":2,"type":0,"version":0}

這說明發號器服務啓動正常,然後使用Linux命令查看服務進程,確保服務進程運行正常:

robert@robert-ubuntu1410:~$ ps -elf | grep java
0 S robert    8244  1847  2  80   0 - 231991 futex_ 21:54 pts/7   00:00:23 java -server -Xms512m -Xmx512m -Xmn128m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -Xloggc:./logs/gc.log -cp /home/robert/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/extlib -jar ./lib/vesta-rest-0.0.1.jar

接下來的章節我們圍繞這個服務來介紹後續的每個腳本和命令。

神奇的腳本解決不可思議的問題

本章介紹那些“神奇的”腳本,這些“神奇的”腳本伴我度過了多少個日日夜夜已經不記得了,凡是線上有緊急問題的時候,或者做一個疑難問題的技術攻關的時候,我都會與他們並肩作戰,同時也爲多個同事解決了不少“不可思議的”問題,因此,這裏我希望能夠把這些腳本分享給每一個奮鬥在一線的技術人員,幫助他們解決手頭上的棘手問題。

如果大家對腳本開發感興趣,可以加入我的開源項目wizard-scripts,我們把實踐過程中使用的腳本逐漸的彙總,最後形成一個專業的開源項目,來幫助更多的同行們,歡迎大家加入開源隊伍。

01

show-busiest-java-threads

此命令通過結合Linux操作系統的ps命令和jvm自帶的jstack命令,查找Java進程內CPU利用率最高的線程,一般適用於服務器負載較高的場景,並需要快速定位導致負載高的原因。

本腳本來自一個叫候鳥樹(筆者在互聯網行業入行前的引路人)的網友,原作者不詳,這裏保留原作者名爲了表示對技術人的尊重。

命令格式:

./show-busiest-java-threads -p 進程號 -c 顯示條數

./show-busiest-java-threads -h

使用示例:

./show-busiest-java-threads -p 8244 -c 3

示例輸出:

robert@robert-ubuntu1410:~/working/scripts$ ./show-busiest-java-threads -p 8244 -c 3
The stack of busy(0.3%) thread(8257/0x2041) of java process(8244) of user(robert):
"C2 CompilerThread1" #7 daemon prio=9 os_prio=0 tid=0xc545d400 nid=0x2041 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

The stack of busy(0.3%) thread(8256/0x2040) of java process(8244) of user(robert):
"C2 CompilerThread0" #6 daemon prio=9 os_prio=0 tid=0xc545bc00 nid=0x2040 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

The stack of busy(0.1%) thread(8260/0x2044) of java process(8244) of user(robert):
"VM Periodic Task Thread" os_prio=0 tid=0xc5463c00 nid=0x2044 waiting on condition

腳本源碼:

#!/bin/bash
# @Function
# Find out the highest cpu consumed threads of java, and print the stack of these threads.
#
# @Usage
#   $ ./show-busy-java-threads
#
# @author Jerry Lee

PROG=`basename $0`

usage() {
    cat < /dev/null; then
    [ -n "$JAVA_HOME" ] && [ -f "$JAVA_HOME/bin/jstack" ] && [ -x "$JAVA_HOME/bin/jstack" ] && {
        export PATH="$JAVA_HOME/bin:$PATH"
    } || {
        redEcho "Error: jstack not found on PATH and JAVA_HOME!"
        exit 1
    }
fi

uuid=`date +%s`_${RANDOM}_$$

cleanupWhenExit() {
    rm /tmp/${uuid}_* &> /dev/null
}
trap "cleanupWhenExit" EXIT

printStackOfThread() {
    while read threadLine ; do
        pid=`echo ${threadLine} | awk '{print $1}'`
        threadId=`echo ${threadLine} | awk '{print $2}'`
        threadId0x=`printf %x ${threadId}`
        user=`echo ${threadLine} | awk '{print $3}'`
        pcpu=`echo ${threadLine} | awk '{print $5}'`
        
        jstackFile=/tmp/${uuid}_${pid}
        
        [ ! -f "${jstackFile}" ] && {
            jstack ${pid} > ${jstackFile} || {
                redEcho "Fail to jstack java process ${pid}!"
                rm ${jstackFile}
                continue
            }
        }
        
        redEcho "The stack of busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) of java process(${pid}) of user(${user}):"
        sed "/nid=0x${threadId0x}/,/^$/p" -n ${jstackFile}
    done
}

[ -z "${pid}" ] && {
    ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk '$4=="java"{print $0}' |
    sort -k5 -r -n | head --lines "${count}" | printStackOfThread
} || {
    ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk -v "pid=${pid}" '$1==pid,$4=="java"{print $0}' |
    sort -k5 -r -n | head --lines "${count}" | printStackOfThread
}

02

find-in-jar

此腳本在Jar包中的包名和類名中查找某一關鍵字,並高亮顯示匹配的Jar包名稱和路徑,多用於定位 java.lang.NoClassDefFoundError 和 java.lang.ClassNotFoundException 的問題,以及類版本重複或者衝突的問題等。

此腳本是筆者的原創,在不同的公司裏幫助很多同事解決了大項目中類版本重複和衝突的問題。

命令格式:

find-in-jar 關鍵字或者類名 路徑

使用示例:

find-in-jar ByteBufferHolder .

示例輸出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ find-in-jar ByteBufferHolder .
./releases/vesta-id-generator-0.0.1-release/lib/tomcat-embed-core-8.0.20.jar
     1165  2015-02-15 18:11   org/apache/coyote/ByteBufferHolder.class
./target/vesta-id-generator-0.0.1-release/lib/tomcat-embed-core-8.0.20.jar
     1165  2015-02-15 18:11   org/apache/coyote/ByteBufferHolder.class

腳本源碼:

#!/bin/bash

find . -name "*.jar" > /tmp/find_in_jar_temp

while read line
do
  if unzip -l $line | grep $1 &> /tmp/find_in_jar_temp_second
  then
    echo $line | sed 's#\(.*\)#\x1b[1;31m\1\x1b[00m#'
    cat /tmp/find_in_jar_temp_second
  fi
done < /tmp/find_in_jar_temp

03

grep-in-jar

次腳本在Jar包中進行二進制內容查找,通常會解決一些線上出現的“不可思議”的問題,例如:某些功能上線沒有生效、某些日誌沒有打印等,通常是上線工具或者上線過程出現了問題,把線上的二進制包拉下來並查找特定的關鍵字來定位問題。

此腳本最初來自於微博同事Axb,後來進行了些許修改,在很多棘手的問題上表現了強大的戰鬥力。

命令格式:

grep-in-jar 關鍵字 路徑

使用示例:

grep-in-jar "vesta" .

示例輸出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ grep-in-jar "vesta" .
find 'vesta' in . 
==> Found "vesta" in ./vesta-sample/vesta-sample-embed/target/vesta-sample-embed-0.0.1.jar
==> Found "vesta" in ./vesta-sample/vesta-sample-embed/target/vesta-sample-embed-0.0.1-sources.jar
==> Found "vesta" in ./vesta-sample/vesta-sample-client/target/vesta-sample-client-0.0.1.jar
==> Found "vesta" in ./vesta-sample/vesta-sample-client/target/vesta-sample-client-0.0.1-sources.jar
==> Found "vesta" in ./vesta-rest-netty/target/vesta-rest-netty-0.0.1-sources.jar
==> Found "vesta" in ./vesta-rest-netty/target/vesta-rest-netty-0.0.1.jar

腳本源碼:

#!/bin/bash
### grep text in jars

if [ $# -lt 2 ];then
    echo 'Usage : jargrep text path'
    exit 1;
fi

LOOK_FOR=$1
LOOK_FOR=`echo ${LOOK_FOR//\./\/}`
folder=$2
echo "find '$LOOK_FOR' in $folder "
for i in `find $2 -name "*jar"`
do
    unzip -p $i | grep "$LOOK_FOR" > /dev/null
    if [ $? = 0   ]
    then
        echo "==> Found \"$LOOK_FOR\" in $i"
    fi
done

04

jar-conflict-detect

此腳本用於識別衝突的Jar包,可以在一個根目錄下找到所有包含相同類的Jar包,並且根據相同類的多少來判斷Jar包的相似度,常常用於某些功能上線不可用或者沒有按照預期起到作用,使用此腳本分析是否存在兩個版本的類,而老版本的類被Java虛擬機加載,其實,JVM規範並沒有規定類路徑下相同類的加載順序,實現JVM規範的虛擬機的實現機制也各不相同,因此無法判斷相同的類中哪個版本的類會被先加載,因此Jar包衝突是個非常討厭的問題。

此腳本最初來自於作者hongjiang,爲了能夠在我的Linux版本上正常運行,後來進行了些許修改,它在很多棘手的問題上表現了強大的戰鬥力。

命令格式:

jar-conflict-detect 路徑

使用示例:

jar-conflict-detect .

示例輸出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib$ jar-conflict-detect .
Similarity  DuplicateClasses  File1                        File2
%21         6                 commons-logging-1.1.3.jar    jcl-over-slf4j-1.7.11.jar
%02         10                commons-beanutils-1.8.0.jar  commons-collections-3.2.1.jar
See /tmp/cp-verbose.log for more details.

腳本源碼:

#!/bin/bash
if [ $# -eq 0 ];then
    echo "please enter classpath dir"
    exit -1
fi

if [ ! -d "$1" ]; then
    echo "not a directory"
    exit -2
fi

tmpfile="/tmp/.cp$(date +%s)"
tmphash="/tmp/.hash$(date +%s)"
verbose="/tmp/cp-verbose.log"

declare -a files=(`find "$1" -name "*.jar"`)
for ((i=0; i < ${#files[@]}; i++)); do
    jarName=`basename ${files[$i]}`
    list=`unzip -l ${files[$i]} | awk -v fn=$jarName '/\.class$/{print $NF,fn}'`
    size=`echo "$list" | wc -l`
    echo $jarName $size >> $tmphash
    echo "$list"
done | sort | awk 'NF{
    a[$1]++;m[$1]=m[$1]","$2}END{for(i in a) if(a[i] > 1) print i,substr(m[i],2)
}' > $tmpfile

awk '{print $2}' $tmpfile |
awk -F',' '{i=1;for(;i<=NF;i++) for(j=i+1;j<=NF;j++) print $i,$j}' |
sort | uniq -c | sort -nrk1 | while read line; do
    dup=${line%% *}
    jars=${line#* }
    jar1=${jars% *}
    jar2=${jars#* }
    len_jar1=`grep -F "$jar1" $tmphash | grep ^"$jar1" | awk '{print $2}'`
    len_jar2=`grep -F "$jar2" $tmphash | grep ^"$jar2" | awk '{print $2}'`
    # Modified by Robert 2017.4.9
    #len=$(($len_jar1 > $len_jar2 ? $len_jar1 : $len_jar2))
    len_jar1=`echo $len_jar1 | awk -F' ' '{print $1}'`
    len_jar2=`echo $len_jar2 | awk -F' ' '{print $1}'`
    if [ $len_jar1 -gt $len_jar2 ]
    then
      len=$len_jar1
    else
      len=$len_jar2
    fi
    per=$(echo "scale=2; $dup/$len" | bc -l)
    echo ${per/./} $dup $jar1 $jar2
done | sort -nr -k1 -k2 |
awk 'NR==1{print "Similarity DuplicateClasses File1 File2"}{print "%"$0}'| column -t

sort $tmpfile | awk '{print $1,"\n\t\t",$2}' > $verbose
echo "See $verbose for more details."

rm -f $tmpfile
rm -f $tmphash

05

http-spy

此腳本利用Linux命令nc檢查HTTP請求參數、請求頭和請求體等信息,常常用於調試基於HTTP協議的服務調用,如果一次HTTP調用沒有的達到預期的效果,首先檢查傳遞的參數是否正確,這包括請求參數、請求頭、請求體等信息,這種場景正式此腳本的用武之地。

命令格式:

http-spy

使用示例:

http-spy

示例輸出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib$ curl "http://localhost:8888?abc=def"
robert@robert-ubuntu1410:~/working/scripts$ ./http-spy 
GET /?abc=def HTTP/1.1
User-Agent: curl/7.35.0
Host: localhost:8888
Accept: */*

腳本源碼:

#!/bin/bash

while true; do nc -l 8888; done

06

show-mysql-qps

此腳本可以用於快速查看Mysql實例的負載情況,包括QPS、TPS、提交數、回滾數、併發線程、執行線程等。

命令格式:

show-mysql-qps 用戶名 密碼

使用示例:

show-mysql-qps root ****

示例輸出:

robert@robert-ubuntu1410:~/working/scripts$ show-mysql-qps.sh root ******
QPS Commit Rollback TPS Threads_con Threads_run 
------------------------------------------------------- 
1      0        0       0        1          1 
1      0        0       0        1          1 
1      0        0       0        1          1 
1      0        0       0        1          1

腳本源碼:

#!/bin/bash
mysqladmin -uroot extended-status -i1 |
awk 'BEGIN{local_switch=0;print "QPS Commit Rollback TPS Threads_con Threads_run \n------------------------------------------------------- "}
$2 ~ /Queries$/ {q=$4-lq;lq=$4;}
$2 ~ /Com_commit$/ {c=$4-lc;lc=$4;}
$2 ~ /Com_rollback$/ {r=$4-lr;lr=$4;
$2 ~ /Threads_connected$/ {tc=$4;}
$2 ~ /Threads_running$/ {tr=$4;
        if(local_switch==0){
          local_switch=1; 
          count=0
        } else {
          if(count>10) {
            count=0;
            print "------------------------------------------------------- \nQPS Commit Rollback TPS Threads_con Threads_run \n------------------------------------------------------- ";
          } else {
            count+=1;
            printf "%-6d %-8d %-7d %-8d %-10d %d \n", q,c,r,c+r,tc,tr;
          }
        }
}'

Java虛擬機相關的命令

本節介紹Java世界裏那些有用的虛擬機命令,有了他們的伴隨,你會感覺到解決Java世界裏面的問題事半功倍。

本章並不會介紹所有的JDK自帶的工具命令,而是結合實踐給讀者介紹那些最常用最重要的Java虛擬機相關的命令,以及一些開源的Java虛擬機工具,目的是幫助大家在現實中解決問題。

如果想全面瞭解JDK各種命令,請參考JDK官方文檔。

01

jad

jad反編譯工具可以將字節碼的二進制類反編譯回Java源代碼,常常用於遇到問題但是無法在源代碼中定位的場景,通過反編譯字節碼可以分析程序事實上執行的流程,從而定位深層次的問題。

由於歷史原因或者其他原因,有些時候項目依賴了沒有源代碼的jar包,如果出現問題需要定位,那麼jad命令可是個小倚天劍。

jad下載地址:JAD

如果不習慣使用命令行,可以下載界面版本jd-gui,可以一次反編譯一個JAR包,並在類之間有簡單的導航操作。

界面版本jd-gui下載地址:JD-GUI

如果開發者對字節碼進行了混淆,反編譯的源代碼將很難讀懂,混淆的代碼只能通過混淆後給出的摘要文件進行分析,這種場景下使用反編譯工具作用不大。

使用示例:

jad AbstractIdServiceImpl.class

示例輸出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib/com/robert/vesta/service/impl$ jad AbstractIdServiceImpl.class
Parsing AbstractIdServiceImpl.class...The class file version is 49.0 (only 45.3, 46.0 and 47.0 are supported)
 Generating AbstractIdServiceImpl.jad
robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib/com/robert/vesta/service/impl$ ll
總用量 40
-rw-rw-r-- 1 robert robert 5375  4月  8 21:33 AbstractIdServiceImpl.class
-rw-rw-r-- 1 robert robert 5197  4月  9 10:01 AbstractIdServiceImpl.jad
-rw-rw-r-- 1 robert robert 2965  4月  8 21:33 IdServiceImpl.class

02

btrace

java應用服務在生產環境中可能會出現各種各樣的問題,有些問題在找到根源原因之前看似很“不可思議”,有些時候並沒有產生異常或者錯誤消息,這時我們無法根據已有的日誌來定位問題,那麼我們需要更多的信息,例如:參數、返回值、程序邏輯判斷、循環次數等信息,來追蹤問題,如果臨時增加日誌,需要重新上線,成本較高,使用遠程調試(後續會在專用文章中介紹gdb等遠程調試工具)又會影響線上流量甚至導致客戶程序超時,這個時候btrace應運而生,btrace可以動態地跟蹤java運行時程序,將定製化的跟蹤字節碼切面注入到運行類中,對運行代碼無侵入,根據官方信息判斷對性能上的影響也可以忽略不計。

瞭解和下載btrace:BTRACE下載主頁

btrace源代碼:BTRACE Github主頁

下面介紹btrace的使用方法:

命令格式:

btrace [-p port] [-cp classpath] pid btrace-script

參數解析:

  1. port指定btrace agent的服務端監聽端口號,供客戶端連接

  1. classpath用來指定依賴的類加載路徑

  2. pid表示進程號,可通過jps或者ps命令獲取

  3. btrace-script即爲btrace跟蹤切面腳本

在運行命令之前,我們需要編寫btrace的跟蹤腳本:

import java.util.Date;

import com.sun.btrace.BTraceUtils;
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace
public class Btrace {

   @OnMethod(
       clazz = "com.cloudate.controller.AdminController",
       method = "sayHello",
       location = @Location(Kind.RETURN)
   )
   public static void sayHello(@Duration long duration) {//單位是納秒,要轉爲毫秒
       println(strcat("duration(ms): ", str(duration / 1000000)));
   }

}

這個跟蹤腳本對業務代碼的方法進行攔截,並打印方法執行時間:

package com.cloudate.controller;

public class AdminController {
    public String sayHello(String name, int age) {
       return "hello everyone";
    }
}

*** 使用示例:***

btrace -p 2020 -cp ~/servlet-api.jar 1507 ~/BTrace.java

當AdminController類的sayHello被調用的時候,控制檯就會打印方法執行時間,這對定位線上的棘手問題有非常大的幫助。

03

jmap

生產中java應用服務發生OutOfMemoryError那是家常便飯,發生了OutOfMemoryError或者發現內存不足報警,我們經常需要找到是什麼原因造成的,要找到內存都去哪兒了,jmap在這個場景是用來定位問題的主要工具,它主要用來查看Java進程內存的使用情況。

jmap是JDK自帶的監控工具,在JDK的根目錄裏可以找到。

使用示例1:

按照佔用空間大小打印程序中類的列表,從這個列表中可以分析哪些類佔用了比較多的內存,再結合代碼找到問題的所在。

jmap -histo:live 2743

示例輸出:

num     #instances         #bytes  class name
----------------------------------------------
   1:         44398        7206296  [C
   2:         23166        2789688  [B
   3:         14511        1276968  java.lang.reflect.Method
   4:         43256         692096  java.lang.String
   5:         22102         530448  org.springframework.boot.loader.util.AsciiBytes
   6:         11047         530256  org.springframework.boot.loader.jar.JarEntryData
   7:          5800         510592  java.lang.Class
   8:         18088         434112  java.util.HashMap$Node
   9:          6465         356336  [Ljava.lang.Object;
  10:         14582         349968  java.util.concurrent.ConcurrentHashMap$Node
  11:          2914         319928  [Ljava.util.HashMap$Node;
  12:          6920         221440  java.util.LinkedHashMap$Entry
  13:          2008         192768  org.springframework.boot.loader.jar.JarEntry
  14:          5691         182112  java.lang.ref.SoftReference
  15:          7467         179208  java.lang.ref.WeakReference
  16:          9410         166712  [Ljava.lang.Class;
  17:          2786         156016  java.util.LinkedHashMap
  18:           101         136256  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  19:          1761         112704  java.lang.reflect.Field
  20:          3081         105056  [Ljava.lang.String;
  21:          4357         104568  java.beans.MethodRef
  ......

使用示例2:

按照佔用空間大小打印程序中加載的動態鏈接庫的列表,其實,Java進程在操作系統中會加載多個動態鏈接庫,Java進程本身和動態鏈接庫都會在其佔用的虛擬地址空間上分配內存,Java堆和棧等內存空間分配在Java進程本身,Java直接內存會分配在Java進程堆內存外或者依賴的動態鏈接庫上,因此,此命令幫助大家定位是Java進程本身佔用內存較大,還是哪個動態鏈接庫佔用內存較多,在定位直接內導致的內存泄露的場景有很大的作用。

jmap 2743

示例輸出:

robert@robert-ubuntu1410:~$ jmap 2743
Attaching to process ID 2743, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.20-b23
0x08048000  5K  /home/robert/working/softwares/jdk1.8.0_20/bin/java
0xe6b2e000  78K /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libnio.so
0xe6d06000  100K    /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libnet.so
0xf65b5000  113K    /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libzip.so
0xf65cf000  41K /lib32/libnss_files-2.19.so
0xf65db000  41K /lib32/libnss_nis-2.19.so
0xf65e7000  89K /lib32/libnsl-2.19.so
0xf6705000  42K /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libmanagement.so
0xf671a000  183K    /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libjava.so
0xf673f000  29K /lib32/librt-2.19.so
0xf6789000  273K    /lib32/libm-2.19.so
0xf67cf000  12213K  /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/server/libjvm.so
0xf759b000  1709K   /lib32/libc-2.19.so
0xf7748000  13K /lib32/libdl-2.19.so
0xf774d000  91K /home/robert/working/softwares/jdk1.8.0_20/lib/i386/jli/libjli.so
0xf7762000  687K    /lib32/libpthread-2.19.so
0xf777e000  29K /lib32/libnss_compat-2.19.so
0xf7789000  54K /home/robert/working/softwares/jdk1.8.0_20/jre/lib/i386/libverify.so
0xf779a000  131K    /lib32/ld-2.19.so

使用示例3:

java堆的內存結構很複雜,包括新生代、老年代、持久代、直接內存等,jmap命令可以查看堆的概要信息。

jmap -heap 38574

示例輸出:

robert@robert-ubuntu1410:~$ jmap -heap 38574
Attaching to process ID 38574, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.65-b01

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 536870912 (512.0MB)
   NewSize                  = 134217728 (128.0MB)
   MaxNewSize               = 134217728 (128.0MB)
   OldSize                  = 402653184 (384.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 120848384 (115.25MB)
   used     = 111727304 (106.55146026611328MB)
   free     = 9121080 (8.698539733886719MB)
   92.45246010074905% used
Eden Space:
   capacity = 107479040 (102.5MB)
   used     = 104015920 (99.19731140136719MB)
   free     = 3463120 (3.3026885986328125MB)
   96.77786478182165% used
From Space:
   capacity = 13369344 (12.75MB)
   used     = 7711384 (7.354148864746094MB)
   free     = 5657960 (5.395851135253906MB)
   57.67959893918505% used
To Space:
   capacity = 13369344 (12.75MB)
   used     = 0 (0.0MB)
   free     = 13369344 (12.75MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 402653184 (384.0MB)
   used     = 15998072 (15.256950378417969MB)
   free     = 386655112 (368.74304962158203MB)
   3.973164161046346% used

15700 interned Strings occupying 2047808 bytes.

使用示例4:

有些Java內存問題不是顯而易見的,從類、動態鏈接庫、堆的概要信息的角度上,無法定位具體產生的原因,我們需要對Java堆的內部結構進行剖析才能進一步分析問題的根源原因,這通常通過jmap命令導出Java堆的快照,然後通過其他工具或者甚至可視化內存分析工具(例如:JHAT、JMAT、JProfiler、Jconsole、JVisualVM)等進行詳細分析。

jmap -dump:format=b,file=./heap.hprof 2743

示例輸出:

robert@robert-ubuntu1410:~$ jmap -dump:format=b,file=./heap.hprof 2743
Dumping heap to /home/robert/heap.hprof ...
Heap dump file created
robert@robert-ubuntu1410:~$ ll
總用量 27184
......
-rw-------  1 robert robert 27632924  4月  9 11:15 heap.hprof

04

jstat

jstat利用了JVM內建的指令對Java應用程序的資源和性能進行實時的命令行的監控,包括了對堆大小和垃圾回收狀況的監控等等,與jmap對比,jstat更傾向於輸出積累的信息與打印GC等的統計信息等。

jstat是JDK自帶的監控工具,在JDK的根目錄裏可以找到。

使用示例:

jstat -gcutil 2743 5000 10

示例輸出:

robert@robert-ubuntu1410:~$ jstat -gcutil 2743 5000 10
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00   0.75   4.82  98.92      -      8    0.174     6    0.332    0.506
  0.00   0.00   0.75   4.82  98.92      -      8    0.174     6    0.332    0.506

名詞解析:

S0C:年輕代中第一個survivor(倖存區)的容量 (字節)

S1C:年輕代中第二個survivor(倖存區)的容量 (字節)

S0U:年輕代中第一個survivor(倖存區)目前已使用空間 (字節)

S1U:年輕代中第二個survivor(倖存區)目前已使用空間 (字節)

EC:年輕代中Eden(伊甸園)的容量 (字節)

EU:年輕代中Eden(伊甸園)目前已使用空間 (字節)

OC:Old代的容量 (字節)

OU:Old代目前已使用空間 (字節)

PC:Perm(持久代)的容量 (字節)

PU:Perm(持久代)目前已使用空間 (字節)

YGC:從應用程序啓動到採樣時年輕代中gc次數

YGCT:從應用程序啓動到採樣時年輕代中gc所用時間(s)

FGC:從應用程序啓動到採樣時old代(全gc)gc次數

FGCT:從應用程序啓動到採樣時old代(全gc)gc所用時間(s)

GCT:從應用程序啓動到採樣時gc用的總時間(s)

NGCMN:年輕代(young)中初始化(最小)的大小 (字節)

NGCMX:年輕代(young)的最大容量 (字節)

NGC:年輕代(young)中當前的容量 (字節)

OGCMN:old代中初始化(最小)的大小 (字節)

OGCMX:old代的最大容量 (字節)

OGC:old代當前新生成的容量 (字節)

PGCMN:perm代中初始化(最小)的大小 (字節)

PGCMX:perm代的最大容量 (字節)

PGC:perm代當前新生成的容量 (字節)

S0:年輕代中第一個survivor(倖存區)已使用的佔當前容量百分比

S1:年輕代中第二個survivor(倖存區)已使用的佔當前容量百分比

E:年輕代中Eden(伊甸園)已使用的佔當前容量百分比

O:old代已使用的佔當前容量百分比

P:perm代已使用的佔當前容量百分比

S0CMX:年輕代中第一個survivor(倖存區)的最大容量 (字節)

S1CMX :年輕代中第二個survivor(倖存區)的最大容量 (字節)

ECMX:年輕代中Eden(伊甸園)的最大容量 (字節)

DSS:當前需要survivor(倖存區)的容量 (字節)(Eden區已滿)

TT: 持有次數限制

MTT : 最大持有次數限制

05

jstack

jstack命令用於打印出給定的java進程ID的線程堆棧快照信息,從而可以看到Java進程內線程的執行狀態、這個你在執行的任務等等,可以據此分析線程等待、死鎖等問題。

jstack也是JDK自帶的命令,在JDK的根目錄裏可以找到。本文第二章“神奇的”腳本中的show-busiest-java-threads腳本也是基於此命令實現的。

使用示例:

jstack 2743

示例輸出:

robert@robert-ubuntu1410:~$ jstack 2743
2017-04-09 12:06:51
Full thread dump Java HotSpot(TM) Server VM (25.20-b23 mixed mode):

"Attach Listener" #23 daemon prio=9 os_prio=0 tid=0xc09adc00 nid=0xb4c waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"http-nio-8080-Acceptor-0" #22 daemon prio=5 os_prio=0 tid=0xc3341000 nid=0xb02 runnable [0xbf1bd000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241)
    - locked <0xcf8938d8> (a java.lang.Object)
    at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:688)
    at java.lang.Thread.run(Thread.java:745)

"http-nio-8080-ClientPoller-1" #21 daemon prio=5 os_prio=0 tid=0xc35bc400 nid=0xb01 runnable [0xbf1fe000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
    at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
    at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:79)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    - locked <0xcf99b100> (a sun.nio.ch.Util$2)
    - locked <0xcf99b0f0> (a java.util.Collections$UnmodifiableSet)
    - locked <0xcf99aff8> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:1052)
    at java.lang.Thread.run(Thread.java:745)
......

06

jinfo

jinfo可以輸出並修改運行時的java進程的環境變量和虛擬機參數。

使用示例:

jinfo 38574

示例輸出:

$ jinfo 38574
     
Attaching to process ID 38574, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.65-b01
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.65-b01
sun.boot.library.path = /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib
java.protocol.handler.pkgs = null|org.springframework.boot.loader
user.country.format = CN
gopherProxySet = false
java.vendor.url = http://java.oracle.com/

......

VM Flags:
Non-default VM flags: -XX:CICompilerCount=3 -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSParallelRemarkEnabled -XX:+DisableExplicitGC -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=536870912 -XX:MaxNewSize=134217728 -XX:MaxTenuringThreshold=6 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=134217728 -XX:OldPLABSize=16 -XX:OldSize=402653184 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:ThreadStackSize=256 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseFastUnorderedTimeStamps -XX:+UseParNewGC 
Command line:  -Xms512m -Xmx512m -Xmn128m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -Xloggc:./logs/gc.lo

07

其他命令

除了上面介紹的常用Java虛擬機相關命令以外,我們還有兩類工具,這節只會做功能介紹,不做詳細的展開。

  1. 基本命令:

    1. javah:生成java類的C頭文件,一般用於開發JNI庫

    2. jps:用來查找Java進程,通常使用ps命令代替

    3. jhat:用來分析內存堆快照文件

    4. jdb:遠程調試,用於線上定位問題

    5. jstatd:jstat的服務器版本

  2. Java虛擬機圖形界面分析工具:

    1. JConsole:JDK自帶的可以查看Java內存和線程堆棧的工具,已經過時

    2. JVisualVM:JDK自帶的可以查看Java內存和線程堆棧的工具,是JConsole的替代版本

    3. JMAT:Eclipse組織開發的全功能的開源Java性能跟蹤、分析和定位工具

    4. JProfiler:全功能的商業化Java性能跟蹤、分析和定位工具

一次OOM定位與修復過程中與監控同事現場編寫的腳本

本節提供一個筆者在實踐過程中解決OOM問題的一個簡單腳本,這個腳本是爲了解決OOM(unable to create native thread)的問題而在問題機器上臨時編寫,並臨時使用的,腳本並沒有寫的很專業,只是爲了抓取需要的信息並解決問題,但是在線上問題十分火急的情況下,這個腳本會有大用處。

#!/bin/bash

ps -Leo pid,lwp,user,pcpu,pmem,cmd >> /tmp/pthreads.log
echo "ps -Leo pid,lwp,user,pcpu,pmem,cmd >> /tmp/pthreads.log" >> /tmp/pthreads.log
echo `date` >> /tmp/pthreads.log
echo 1

pid=`ps aux|grep tomcat|grep cwh|awk -F ' ' '{print $2}'`
echo 2

echo "pstack $pid >> /tmp/pstack.log" >> /tmp/pstack.log
pstack $pid >> /tmp/pstack.log
echo `date` >> /tmp/pstack.log
echo 3

echo "lsof >> /tmp/sys-o-files.log" >> /tmp/sys-o-files.log
lsof >> /tmp/sys-o-files.log
echo `date` >> /tmp/sys-o-files.log
echo 4

echo "lsof -p $pid >> /tmp/service-o-files.log" >> /tmp/service-o-files.log
lsof -p $pid >> /tmp/service-o-files.log
echo `date` >> /tmp/service-o-files.log
echo 5

echo "jstack -l $pid  >> /tmp/js.log" >> /tmp/js.log
jstack -l -F $pid  >> /tmp/js.log
echo `date` >> /tmp/js.log
echo 6 

echo "free -m >> /tmp/free.log" >> /tmp/free.log
free -m >> /tmp/free.log
echo `date` >> /tmp/free.log
echo 7

echo "vmstat 2 1 >> /tmp/vm.log" >> /tmp/vm.log
vmstat 2 1 >> /tmp/vm.log
echo `date` >> /tmp/vm.log
echo 8

echo "jmap -dump:format=b,file=/tmp/heap.hprof 2743" >> /tmp/jmap.log
jmap -dump:format=b,file=/tmp/heap.hprof >> /tmp/jmap.log
echo `date` >> /tmp/jmap.log
echo 9

echo end

對於這次線上產生的OOM問題,與運維和監控部們的同事奮鬥了幾天幾夜,終於通過在線上抓取信息、分析問題、在性能壓測部門同事的幫助下,最小化重現問題並找到問題的根源原因,最後,針對問題產生的根源提供了有效的方案,隨後,會在新的一篇文章中專門討論如何解決線上的OutOfMemeoryError的問題,這篇新文章將會包括OutOfMemoryError的種類,對於每種OutOfMemoryError產生的原因,以及解決的辦法等。

而本節只給讀者介紹這個看似簡陋而又信息滿滿的Java服務的監控腳本,如果讀者在線上已經遇到了OOM的問題,可以順着這個腳本的思路,利用本文提供的各種腳本和命令來深挖問題的根本原因。

場景與命令彙總表

正如文章開始提到,我會把所有的命令和腳本收集在一個表格中,稱爲“場景與命令彙總表”,便於大家隨時參考和使用,並推薦大家把這個表格打印出來放在自己的辦公桌上,需要的時候看一眼,便可快速發現和解決問題的工具。

序號 場景 腳本
1 服務器負載高、服務超時、CPU利用率高 show-busiest-java-threads
2 java.lang.NoClassDefFoundError、java.lang.ClassNotFoundException、程序未按照預期運行 find-in-jar
3 程序未按照預期運行、上線後未執行新邏輯、查找某些關鍵字 grep-in-jar
4 Jar包版本衝突、程序未按照預期運行 jar-conflict-detect
5 HTTP調用後發現未按照預期輸出結果 http-spy
6 數據庫負載高、SQL超時 show-mysql-qps
7 沒有源碼的Jar包出了問題、破解別人的代碼 jad
8 線上出問題還無法上線打點日誌、線上調試、做切面 btrace
9 內存不足、OutOfMemoryError jmap
10 內存不足、OutOfMemoryError、GC頻繁、服務超時、響應長尾 jstat
11 服務超時、線程死鎖、服務器負載高 jstack
12 查看或者修改Java進程環境變量和Java虛擬機變量 jinfo
13 使用JNI開發Java本地程序庫 javah
14 查找java進程ID jps
15 分析jmap產生的java堆的快照 jhat
16 QA環境無法重現,需要在準生產線上遠程調試 jdb
17 與jstat相同,但是可以在線下用客戶端連接,可線下操作 jstatd
18 簡單的有界面的內存分析工具,JDK自帶 JConsole
19 全面的有界面的內存分析工具,JDK自帶 JVisualVM
20 專業的Java進程性能分析和跟蹤工具 JMAT
21 商業化的Java進程性能分析和跟蹤工具 JProfiler

總結經驗

本文開始介紹了學習那些高效應用層腳本和Java虛擬機命令的示例服務Vesta的配置和上線,然後,在以Vesta服務運行爲背景下重點介紹了筆者積累的高效的應用層腳本,可以幫助讀者解決服務負載高、JAR包衝突、驗證線上服務代碼、動態添加線上日誌的問題,然後給讀者介紹了關鍵的幾個Java虛擬機命令,幫助大家查看Java虛擬機運行狀態、線程堆棧、內存使用情況、GC頻率等,能夠幫助大家對自己的服務保駕護航。

正如文章開始提到,另外一篇文章聚焦在線上應急和技術攻關過程中,你必須學會使用的那些Linux基礎命令,將會盡快在後續的文章中與讀者見面,敬請期待。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章