在日常Java開發(fā)中,JVM工具是分析各種線上問題的利器。本文重點(diǎn)介紹常見的JVM工具的簡單使用:jinfo,jps,jstack,jstat,jmap,jmap,jconsole,jvisualvm,jprofiler,還有阿里巴巴開源的 arthas,這些工具的組合使用將大大提高JVM內(nèi)存問題分析定位的效率。
jvm監(jiān)控分析工具一般分為兩類,一種是jdk自帶的工具,一種是第三方的分析工具。jdk自帶工具一般在jdk bin目錄下面,以exe的形式直接點(diǎn)擊就可以使用,其中包含分析工具已經(jīng)很強(qiáng)大,幾乎涉及了方方面面,但是我們最常使用的只有兩款:jconsole.exe和jvisualvm.exe;第三方的分析工具有很多,各自的側(cè)重點(diǎn)不同,比較有代表性如:jprofiler、arthas、MAT(Memory Analyzer Tool)、GChisto等。
- jps(Java Virtual Machine Process Status Tool): 查看所有的jvm進(jìn)程,包括進(jìn)程ID,進(jìn)程啟動的路徑等等。
- jinfo(Java Configuration Info): 觀察進(jìn)程運(yùn)行環(huán)境參數(shù),包括Java System屬性和JVM命令行參數(shù)
- jstack(Java Stack Trace): 查看jvm中當(dāng)前所有線程棧的運(yùn)行情況和線程當(dāng)前狀態(tài)
- jstat(Java Virtual Machine Statistics Monitoring Tool):監(jiān)視JVM內(nèi)存資源,包括各種堆和非堆的大小及其內(nèi)存使用量
- jmap(Java Memory Map):監(jiān)視進(jìn)程運(yùn)行中的jvm物理內(nèi)存的占用情況,該進(jìn)程內(nèi)存內(nèi),所有對象的情況,例如產(chǎn)生了哪些對象,對象數(shù)量。
- jconsole
- jvisualvm
- jprofiler: JVM性能分析工具
- arthas: 阿里巴巴開源的JVM問題分析神器。
jstack
觀察jvm中當(dāng)前所有線程的運(yùn)行情況和線程當(dāng)前狀態(tài)。
如果java程序崩潰生成core文件,jstack工具可以用來獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發(fā)生問題。jstack工具還可以附屬到正在運(yùn)行的java程序中,看到當(dāng)時運(yùn)行的java程序的java stack和native stack的信息, 如果現(xiàn)在運(yùn)行的java程序呈現(xiàn)hung的狀態(tài),jstack是非常有用的。目前只有在Solaris和Linux的JDK版本里面才有。
參數(shù)很簡單,查看幫助 jstack -h
舉例:
jstack pid
jps
列出系統(tǒng)中所有的java進(jìn)程
jps
##################
2306 Bootstrap
3370 Jps 2058 xxxxxxxxx
jinfo
觀察進(jìn)程運(yùn)行環(huán)境參數(shù),包括Java System屬性和JVM命令行參數(shù)
#查看java進(jìn)程的配置信息
jinfo 2058
################
Attaching to process ID 2058, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.0-b56
Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment
project.name = Amoeba-MySQL
java.vm.version = 24.0-b56
sun.boot.library.path = /usr/local/java/jdk1.7/jre/lib/amd64
................................................
# 查看2058的MaxPerm大小可以用
jinfo -flag MaxPermSize 2058
#####################
-XX:MaxPermSize=100663296
jstat
- jstat利用JVM內(nèi)建的指令對Java應(yīng)用程序的資源和性能進(jìn)行實(shí)時的命令行的監(jiān)控,包括了對進(jìn)程的classloader,compiler,gc情況;
- 監(jiān)視JVM內(nèi)存內(nèi)的各種堆和非堆的大小及其內(nèi)存使用量,以及加載類的數(shù)量。
-help 顯示幫助信息。
-version 顯示版本信息
-options 顯示統(tǒng)計選項列表。
outputOptions
參數(shù):
-class:統(tǒng)計類裝載器的行為
-compiler:統(tǒng)計HotSpot Just-in-Time編譯器的行為
-gc:統(tǒng)計堆各個分區(qū)的使用情況
-gccapacity:統(tǒng)計新生區(qū),老年區(qū),permanent區(qū)的heap容量情況
-gccause:統(tǒng)計最后一次gc和當(dāng)前gc的原因
-gcnew:統(tǒng)計gc時,新生代的情況
-gcnewcapacity:統(tǒng)計新生代大小和空間
-gcold:統(tǒng)計老年代和永久代的行為
-gcoldcapacity:統(tǒng)計老年代大小
-gcpermcapacity:統(tǒng)計永久代大小
-gcutil:統(tǒng)計gc時,heap情況
-printcompilation:HotSpot編譯方法統(tǒng)計
-class
#每隔1秒監(jiān)控一次,一共做10次
[root@lq225 conf]# jstat -class 2058 1000 10
Loaded Bytes Unloaded Bytes Time
1697 3349.5 0 0.0 1.79
1697 3349.5 0 0.0 1.79
1697 3349.5 0 0.0 1.79
1697 3349.5 0 0.0 1.79
...................................................
################## 術(shù)語分隔符 ##################
#Loaded 類加載數(shù)量
#Bytes 加載的大?。╧)
#Unloaded 類卸載的數(shù)量
#Bytes 卸載的大?。╧)
#Time 時間花費(fèi)在執(zhí)行類加載和卸載操作
-compiler
Compiled Failed Invalid Time FailedType FailedMethod
302 0 0 1.27 0
.....................................................
################## 術(shù)語分隔符 ##################
#Compiled 編譯任務(wù)的執(zhí)行次數(shù)
#Failed 編譯任務(wù)的失敗次數(shù)
#Invalid 編譯任務(wù)無效的次數(shù)
#Time 編譯任務(wù)花費(fèi)的時間
#FailedType 最后一次編譯錯誤的類型
#FailedMethod 最后一次編譯錯誤的類名和方法
-gc
#每隔2秒監(jiān)控一次,共20次
[root@lq225 conf]# jstat -gc 2058 2000 20
#######################
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
8704.0 8704.0 805.5 0.0 69952.0 64174.5 174784.0 2644.5 16384.0 10426.7 2 0.034 0 0.000 0.034
8704.0 8704.0 805.5 0.0 69952.0 64174.5 174784.0 2644.5 16384.0 10426.7 2 0.034 0 0.000 0.034
8704.0 8704.0 805.5 0.0 69952.0 64174.5 174784.0 2644.5 16384.0 10426.7 2 0.034 0 0.000 0.034
.............................................
################## 術(shù)語分隔符 ##################
#S0C 生還者區(qū)0 容量(KB)
#S1C 生還者區(qū)1 容量(KB)
#S0U 生還者區(qū)0 使用量(KB)
#S1U 生還者區(qū)1 使用量(KB)
#EC 年輕區(qū)Eden容量(KB)
#EU 年輕區(qū)Eden使用量(KB)
#OC 老年區(qū)容量(KB)
#OU 老年區(qū)使用量(KB)
#PC 永久區(qū)容量(KB)
#PU 永久區(qū)使用量(KB)
#YGC 新生代GC次數(shù)
#YGCT 新生代GC時間
#FGC full GC 事件的次數(shù)
#FGCT full GC的時間
#GCT 總GC時間
-gccapacity
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC PGCMN PGCMX PGC PC YGC FGC
131072.0 131072.0 131072.0 13056.0 13056.0 104960.0 393216.0 393216.0 393216.0 393216.0 65536.0 65536.0 65536.0 65536.0 1 0
..........................................................................................................
################## 術(shù)語分隔符 ##################
#NGCMN 最小新生代容量(KB)
#NGCMX 最大新生代容量(KB)
#NGC 當(dāng)前新生代容量(KB)
#S0C 當(dāng)前生存者0區(qū)容量(KB)
#S1C 當(dāng)前生存者1區(qū)容量(KB)
#OGCMN 老年代最小容量(KB)
#OGCMX 老年代最大容量(KB)
#OGC 當(dāng)前老年代容量(KB)
#OC 當(dāng)前老年代,Current old space capacity (KB)
#PGCMN 永久區(qū)最小容量(KB)
#PGCMX 永久區(qū)最大容量(KB)
#PGC 當(dāng)前永久區(qū)容量(KB)
#PC 當(dāng)前永久區(qū),Current Permanent space capacity (KB)
#YGC young GC事件的次數(shù)
#FGC Full GC次數(shù)
-gccause
S0 S1 E O P YGC YGCT FGC FGCT GCT LGCC GCC
0.00 99.84 12.76 0.92 46.23 1 0.016 0 0.000 0.016 unknown GCCause No GC
................................................
################## 術(shù)語分隔符 ##################
#S0 年輕代中第一個survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
#S1 年輕代中第二個survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
#E 年輕代中Eden已使用的占當(dāng)前容量百分比
#O old代已使用的占當(dāng)前容量百分比
#P perm代已使用的占當(dāng)前容量百分比
#YGC 從應(yīng)用程序啟動到采樣時年輕代中g(shù)c次數(shù)
#YGCT 從應(yīng)用程序啟動到采樣時年輕代gc所用時間(s)
#FGC 從應(yīng)用程序啟動到采樣時old代(全gc)gc次數(shù)
#FGCT 從應(yīng)用程序啟動到采樣時old代(全gc)gc所用時間(s)
#GCT 從應(yīng)用程序啟動到采樣時gc用的總時間(s)
#LGCC 最后一次GC的原因
#GCC 當(dāng)前GC的原因
-gcutil
#每隔1秒監(jiān)控一次,共10次
jstat -gcutil 2058 1000 10
########################
[root@lq225 conf]# jstat -gcutil 2058 1000 10
S0 S1 E O P YGC YGCT FGC FGCT GCT
9.25 0.00 96.73 1.51 63.64 2 0.034 0 0.000 0.034
9.25 0.00 96.73 1.51 63.64 2 0.034 0 0.000 0.034
9.25 0.00 96.73 1.51 63.64 2 0.034 0 0.000 0.034
9.25 0.00 96.73 1.51 63.64 2 0.034 0 0.000 0.034
jmap
- 監(jiān)視進(jìn)程運(yùn)行中的jvm物理內(nèi)存的占用情況和在該進(jìn)程內(nèi)存內(nèi),所有對象的情況,例如產(chǎn)生了哪些對象,對象數(shù)量。
- 使用hprof二進(jìn)制形式,輸出jvm的heap內(nèi)容到文件。然后使用jhat工具進(jìn)行分析,在OOM(內(nèi)存溢出)時,分析大對象非常有用。
#參數(shù)
-dump:[live,]format=b,file=<filename> 使用hprof二進(jìn)制形式,輸出jvm的heap內(nèi)容到文件=. live子選項是可選的,假如指定live選項,那么只輸出活的對象到文件.
-finalizerinfo 打印正等候回收的對象的信息.
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情況.
-histo[:live] 打印每個class的實(shí)例數(shù)目,內(nèi)存占用,類全名信息. JVM的內(nèi)部類名字開頭會加上前綴"*". 如果live子參數(shù)加上后,只統(tǒng)計活的對象數(shù)量.
-permstat 打印classload和jvm heap長久層的信息. 包含每個classloader的名字,活潑性,地址,父classloader和加載的class數(shù)量. 另外,內(nèi)部String的數(shù)量和占用內(nèi)存數(shù)也會打印出來.
-F 強(qiáng)迫.在pid沒有相應(yīng)的時候使用-dump或者-histo參數(shù). 在這個模式下,live子參數(shù)無效.
-h | -help 打印輔助信息
-J 傳遞參數(shù)給jmap啟動的jvm.
pid 需要被打印信息的java進(jìn)程id.
-histo
jmap -histo 2058
#####################
num #instances #bytes class name
----------------------------------------------
1: 206 3585312 [I
2: 19621 2791880 <constMethodKlass>
3: 19621 2520048 <methodKlass>
4: 21010 2251616 [C
............................................................
-dump:
#將堆輸出到hprof文件。可以使用jhat工具進(jìn)行分析,在OOM(內(nèi)存溢出)時,分析大對象非常有用
jmap -dump:live,format=b,file=data.hprof 2058
#通過使用如下參數(shù)啟動JVM,也可以獲取到dump文件:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./java_pid<pid>.hprof
jhat
#如果在虛擬機(jī)中導(dǎo)出的heap信息文件可以拿到WINDOWS上進(jìn)行分析,可以查找諸如內(nèi)存方面的問題,如:
jhat data.hprof
#執(zhí)行成功后,訪問http://localhost:7000即可查看內(nèi)存信息。
jconsole
jconsole 是一個java GUI監(jiān)視工具,可以以圖表化的形式顯示各種數(shù)據(jù)。并可通過遠(yuǎn)程連接監(jiān)視遠(yuǎn)程的服務(wù)器VM。 在jdk安裝目錄找到j(luò)console.exe,雙擊即可啟動。
方式一:指定密碼,增加安全性
java -Dcom.sun.management.jmxremote.port=5000
-Dcom.sun.management.jmxremote.password.file=/Users/aihe/Documents/jmxremote.password
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.access.file=/Users/aihe/Documents/jmxremote.access
-jar target/jmxdemo-0.0.1-SNAPSHOT.jar



jvisualvm
jconsole 和 jvisualvm 一樣,也是一個java GUI監(jiān)視工具,可以以圖表化的形式顯示各種數(shù)據(jù)。在jdk安裝目錄找到j(luò)visualvm.exe,雙擊即可啟動。
使用visualvm監(jiān)控tomcat,修改catalina.sh,添加下面一行:
CATALINA_OPTS="$CATALINA_OPTS \
-Dcom.sun.management.jmxremote=true \
-Djava.rmi.server.hostname=192.168.55.255 \
-Dcom.sun.management.jmxremote.port=8086 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false"
注意點(diǎn):
1、用 hostname -i 查看是否為127.0.01,如果是,則必須配置 -Djava.rmi.server.hostname 為本機(jī)IP。
2、檢查防火墻(iptables)是否開啟,以及是否開放jmxremote.port所指定的端口。



以上這些工具都是 JDK 內(nèi)置工具。
jconsole 和 jvisualvm 都是通過jmx來訪問JVM然后進(jìn)行統(tǒng)計的,在啟動JVM的時候,要指定jmx的內(nèi)容。
JProfiler
JProfiler是由ej-technologies GmbH公司開發(fā)的一款性能瓶頸分析工具.有以下特點(diǎn)
- 對被分析的應(yīng)用影響小
- CPU,Thread,Memory分析功能尤其強(qiáng)大
- 支持對jdbc,noSql, jsp, servlet, socket等進(jìn)行分析
- 支持多種模式(離線,在線)的分析

A1. 分析的數(shù)據(jù)主要來自于兩部分
- 一部分來自于jvm的分析接口JVMTI(JVM Tool Interface) , JDK必須>=1.6. 主要采集 例如: 對象的生命周期,thread的生命周期等信息
JVMTI is an event-based system. The profiling agent library can register handler functions for different events. It can then enable or disable selected events
- 一部分來自于instruments classes(可理解為class的重寫,增加JProfiler相關(guān)統(tǒng)計功能):主要采集 例如:方法執(zhí)行時間,次數(shù),方法棧等信息
2. 數(shù)據(jù)收集的原理
- 用戶在JProfiler GUI中下達(dá)監(jiān)控的指令(一般就是點(diǎn)擊某個按鈕)
- JProfiler GUI JVM 通過socket(默認(rèn)端口8849),發(fā)送指令給被分析的jvm中的JProfile Agent。
- JProfiler Agent(如果不清楚Agent請看文章第三部分"啟動模式") 收到指令后,將該指令轉(zhuǎn)換成相關(guān)需要監(jiān)聽的事件或者指令,來注冊到JVMTI上或者直接讓JVMTI去執(zhí)行某功能(例如dump jvm內(nèi)存)
- JVMTI 根據(jù)注冊的事件,來收集當(dāng)前jvm的相關(guān)信息。 例如: 線程的生命周期; jvm的生命周期;classes的生命周期;對象實(shí)例的生命周期;堆內(nèi)存的實(shí)時信息等等
- JProfiler Agent將采集好的信息保存到內(nèi)存中,按照一定規(guī)則統(tǒng)計好(如果發(fā)送所有數(shù)據(jù)JProfiler GUI,會對被分析的應(yīng)用網(wǎng)絡(luò)產(chǎn)生比較大的影響)
- 返回給JProfiler GUI Socket.
- JProfiler GUI Socket 將收到的信息返回 JProfiler GUI Render
- JProfiler GUI Render 渲染成最終的展示效果
Arthas
Arthas 是阿里開源的一款好用的jvm監(jiān)控工具,有點(diǎn)像是把jdk中自帶的命令行工具做了集合。
安裝
# 安裝方式一
curl -L https://alibaba.github.io/arthas/install.sh | sh
# 安裝方式二
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
啟動
啟動 arthas-boot.jar
java -jar arthas-boot.jar

參考
- JDK內(nèi)置工具使用
- JVM監(jiān)控工具
- Arthas官方文檔
- Java性能分析神器-JProfiler詳解(一)
- Intellij IDEA集成JProfiler性能分析神器
- jvm系列(七):jvm調(diào)優(yōu)-工具篇
- Jvisualvm簡單使用教程
附錄
一些中文解釋
S0C:年輕代中第一個survivor(幸存區(qū))的容量 (字節(jié))
S1C:年輕代中第二個survivor(幸存區(qū))的容量 (字節(jié))
S0U:年輕代中第一個survivor(幸存區(qū))目前已使用空間 (字節(jié))
S1U:年輕代中第二個survivor(幸存區(qū))目前已使用空間 (字節(jié))
EC:年輕代中Eden的容量 (字節(jié))
EU:年輕代中Eden目前已使用空間 (字節(jié))
OC:Old代的容量 (字節(jié))
OU:Old代目前已使用空間 (字節(jié))
PC:Perm(持久代)的容量 (字節(jié))
PU:Perm(持久代)目前已使用空間 (字節(jié))
YGC:從應(yīng)用程序啟動到采樣時年輕代中g(shù)c次數(shù)
YGCT:從應(yīng)用程序啟動到采樣時年輕代中g(shù)c所用時間(s)
FGC:從應(yīng)用程序啟動到采樣時old代(全gc)gc次數(shù)
FGCT:從應(yīng)用程序啟動到采樣時old代(全gc)gc所用時間(s)
GCT:從應(yīng)用程序啟動到采樣時gc用的總時間(s)
NGCMN:年輕代(young)中初始化(最小)的大小 (字節(jié))
NGCMX:年輕代(young)的最大容量 (字節(jié))
NGC:年輕代(young)中當(dāng)前的容量 (字節(jié))
OGCMN:old代中初始化(最小)的大小 (字節(jié))
OGCMX:old代的最大容量 (字節(jié))
OGC:old代當(dāng)前新生成的容量 (字節(jié))
PGCMN:perm代中初始化(最小)的大小 (字節(jié))
PGCMX:perm代的最大容量 (字節(jié))
PGC:perm代當(dāng)前新生成的容量 (字節(jié))
S0:年輕代中第一個survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
S1:年輕代中第二個survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
E:年輕代中Eden(伊甸園)已使用的占當(dāng)前容量百分比
O:old代已使用的占當(dāng)前容量百分比
P:perm代已使用的占當(dāng)前容量百分比
S0CMX:年輕代中第一個survivor(幸存區(qū))的最大容量 (字節(jié))
S1CMX:年輕代中第二個survivor(幸存區(qū))的最大容量 (字節(jié))
ECMX:年輕代中Eden(伊甸園)的最大容量 (字節(jié))
DSS:當(dāng)前需要survivor(幸存區(qū))的容量 (字節(jié))(Eden區(qū)已滿)
TT: 持有次數(shù)限制
MTT : 最大持有次數(shù)限制