JVM性能調優監控命令jps、jinfo、jstat、jmap+jhat、jstack使用詳解


JDK本身提供了很多方便的JVM性能調優監控工具,除了集成式的VisualVM和jConsole外,還有jps、jinfo、jstat、jmap+jhat、jstack等小巧的工具,本博客希望能起拋磚引玉之用,讓大家能開始對JVM性能調優的常用工具有所了解。

    現實企業級Java開發中,有時候我們會碰到下面這些問題:

  • OutOfMemoryError,內存不足

  • 內存泄露

  • 線程死鎖

  • 鎖爭用(Lock Contention)

  • Java進程消耗CPU過高

  • ......

    這些問題在日常開發中可能被很多人忽視(比如有的人遇到上面的問題只是重啟服務器或者調大內存,而不會深究問題根源),但能夠理解並解決這些問題是Java程序員進階的必備要求。本文將對一些常用的JVM性能調優監控工具進行介紹,希望能起拋磚引玉之用。本文參考了網上很多資料,難以一一列舉,在此對這些資料的作者表示感謝!關於JVM性能調優相關的資料,請參考文末。

jps – 用來查看JVM里面所有進程的具體狀態, 包括進程ID,進程啟動的路徑等等。

jinfo –可以知道崩潰的JVM參數配置信息及JDK版本安裝路徑等信息。

jstat – JVM內建的指令對Java應用程序的資源和性能進行實時的命令行的監控,包括了對Heap size和垃圾回收狀況的監控等等。

jmap+jhat –jmap可以生成堆轉儲快照文件,jhat可以查看快照文件分析內存溢出可能的原因。

jstack -- 獲取JVM當前線程,JVM內每個線程正在執行方法的棧信息,包括線程狀態,什么線程操作導致當前線程狀態。

jdb – jdb 用來對core文件和正在運行的Java進程進行實時地調試,里面包含了豐富的命令幫助您進行調試,它的功能和Sun studio里面所帶的dbx非常相似,但 jdb是專門用來針對Java應用程序的。

jconsole – jconsole是基於Java Management Extensions (JMX)的實時圖形化監測工具,這個工具利用了內建到JVM里面的JMX指令來提供實時的性能和資源的監控,包括了Java程序的內存使用,Heap size, 線程的狀態,類的分配狀態和空間使用等等。

1、 jps(JVM Process Status Tool)      

    jps主要用來輸出JVM中運行的進程狀態信息。語法格式如下:

1 jps [options] [hostid]

 

    如果不指定hostid就默認為當前主機或服務器。

    命令行參數選項說明如下:

1 -q 不輸出類名、Jar名和傳入main方法的參數
2 -m 輸出傳入main方法的參數
3 -l 輸出main類或Jar的全限名
4 -v 輸出傳入JVM的參數

 

   比如下面:

1 root@ubuntu:/# jps -m -l
2 2458 org.artifactory.standalone.main.Main /usr/local/artifactory-2.2.5/etc/jetty.xml
3 29920 com.sun.tools.hat.Main -port 9998 /tmp/dump.dat
4 3149 org.apache.catalina.startup.Bootstrap start
5 30972 sun.tools.jps.Jps -m -l
6 8247 org.apache.catalina.startup.Bootstrap start
7 25687 com.sun.tools.hat.Main -port 9999 dump.dat
8 21711 mrf-center.jar

 2、jinfo(JVM Configuration Info)

描述:輸出給定 java 進程所有的配置信息。包括 java 系統屬性和 jvm 命令行標記等。

用法:

jinfo [ option ] pid

jinfo [ option ] executable core

jinfo [ option ] [server-id@]remote-hostname-or-IP

例子:

jinfo -flag MaxHeapSize 10212

得到結果如下:

-XX:MaxHeapSize=786432000

 

jinfo 10212

得到結果如下:

Attaching to process ID 10212, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.101-b13
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.101-b13
sun.boot.library.path = D:\MyAPPs\Java\jdk1.8.0_101\jre\bin
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = ;
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level =
sun.java.launcher = SUN_STANDARD
user.script =
user.country = CN
user.dir = E:\projects\git\t-io
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_101-b13
java.awt.graphicsenv = sun.awt.Win32GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = D:\MyAPPs\Java\jdk1.8.0_101\jre\lib\endorsed
line.separator =

java.io.tmpdir = C:\Users\Barry\AppData\Local\Temp\
java.vm.specification.vendor = Oracle Corporation
user.variant =
os.name = Windows 7
sun.jnu.encoding = GBK
java.library.path = D:\MyAPPs\Java\jdk1.8.0_101\bin;...
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 6.1
user.home = C:\Users\Barry
user.timezone = Asia/Shanghai
java.awt.printerjob = sun.awt.windows.WPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = Barry
java.class.path =
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = org.tio.examples.helloworld.client.HelloClientStarter
java.home = D:\MyAPPs\Java\jdk1.8.0_101\jre
user.language = zh
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.windows.WToolkit
java.vm.info = mixed mode
java.version = 1.8.0_101
java.ext.dirs = D:\MyAPPs\Java\jdk1.8.0_101\jre\lib\ext;C:\Windows\Sun\Java\lib\
ext
sun.boot.class.path = D:\MyAPPs\Java\jdk1.8.0_101\jre\lib\resources.jar;..
file.separator = \
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.desktop = windows
sun.cpu.isalist = amd64

VM Flags:
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=134217728 -XX:Ma
xHeapSize=786432000 -XX:MaxNewSize=262144000 -XX:MinHeapDeltaBytes=524288 -XX:Ne
wSize=44564480 -XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseComp
ressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocatio
n -XX:+UseParallelGC
Command line: -Xms128m -Xmx750m -javaagent:D:\MyAPPs\IntelliJ IDEA 2017.1.1\lib
\idea_rt.jar=52309:D:\MyAPPs\IntelliJ IDEA 2017.1.1\bin -Dfile.encoding=UTF-8

這個命令包含了 JDK 和 JVM 運行起來時的一些屬性。

3、jstat(JVM statistics Monitoring Tool統計監測工具,比如新生代,老年代內存使用情況)

    語法格式如下:

1 jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]

 

    vmid是虛擬機ID,在Linux/Unix系統上一般就是進程ID。interval是采樣時間間隔。count是采樣數目。比如下面輸出的是GC信息,采樣時間間隔為250ms,采樣數為4:

1 root@ubuntu:/# jstat -gc 21711 250 4
2  S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
3 192.0  192.0   64.0   0.0    6144.0   1854.9   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
4 192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
5 192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
6 192.0  192.0   64.0   0.0    6144.0   2109.7   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649

 

    要明白上面各列的意義,先看JVM堆內存布局:

JVM性能調優監控工具jps、jstack、jmap、jhat、jstat使用詳解

    可以看出:

1 堆內存 = 年輕代 + 年老代 + 永久代
2 年輕代 = Eden區 + 兩個Survivor區(From和To)

 

    現在來解釋各列含義:

1 S0C、S1C、S0U、S1U:Survivor 0/1區容量(Capacity)和使用量(Used)
2 EC、EU:Eden區容量和使用量
3 OC、OU:年老代容量和使用量
4 PC、PU:永久代容量和使用量
5 YGC、YGT:年輕代GC次數和GC耗時
6 FGC、FGCT:Full GC次數和Full GC耗時
7 GCT:GC總耗時

 

 

4、 jmap(Memory Map)和jhat(Java Heap Analysis Tool)

    jmap用來查看堆內存使用狀況,生成堆轉儲快照,一般用jhat查看jmap生成的快照。

    jmap語法格式如下:

1 jmap [option] pid
2 jmap [option] executable core
3 jmap [option] [server-id@]remote-hostname-or-ip

    如果運行在64位JVM上,可能需要指定-J-d64命令選項參數。

1 jmap -permstat pid

    打印進程的類加載器和類加載器加載的持久代對象信息,輸出:類加載器名稱、對象是否存活(不可靠)、對象地址、父類加載器、已加載的類大小等信息,如下圖:

JVM性能調優監控工具jps、jstack、jmap、jhat、jstat使用詳解

   使用jmap -heap pid查看進程堆內存使用情況,包括使用的GC算法、堆配置參數和各代中堆內存使用情況。比如下面的例子:

01 root@ubuntu:/# jmap -heap 21711
02 Attaching to process ID 21711, please wait...
03 Debugger attached successfully.
04 Server compiler detected.
05 JVM version is 20.10-b01
06  
07 using thread-local object allocation.
08 Parallel GC with 4 thread(s)
09  
10 Heap Configuration:
11    MinHeapFreeRatio = 40
12    MaxHeapFreeRatio = 70
13    MaxHeapSize      = 2067791872 (1972.0MB)
14    NewSize          = 1310720 (1.25MB)
15    MaxNewSize       = 17592186044415 MB
16    OldSize          = 5439488 (5.1875MB)
17    NewRatio         = 2
18    SurvivorRatio    = 8
19    PermSize         = 21757952 (20.75MB)
20    MaxPermSize      = 85983232 (82.0MB)
21  
22 Heap Usage:
23 PS Young Generation
24 Eden Space:
25    capacity = 6422528 (6.125MB)
26    used     = 5445552 (5.1932830810546875MB)
27    free     = 976976 (0.9317169189453125MB)
28    84.78829520089286% used
29 From Space:
30    capacity = 131072 (0.125MB)
31    used     = 98304 (0.09375MB)
32    free     = 32768 (0.03125MB)
33    75.0% used
34 To Space:
35    capacity = 131072 (0.125MB)
36    used     = 0 (0.0MB)
37    free     = 131072 (0.125MB)
38    0.0% used
39 PS Old Generation
40    capacity = 35258368 (33.625MB)
41    used     = 4119544 (3.9287033081054688MB)
42    free     = 31138824 (29.69629669189453MB)
43    11.683876009235595% used
44 PS Perm Generation
45    capacity = 52428800 (50.0MB)
46    used     = 26075168 (24.867218017578125MB)
47    free     = 26353632 (25.132781982421875MB)
48    49.73443603515625% used
49    ....

    使用jmap -histo[:live] pid查看堆內存中的對象數目、大小統計直方圖,如果帶上live則只統計活對象,如下:

01 root@ubuntu:/# jmap -histo:live 21711 | more
02  
03  num     #instances         #bytes  class name
04 ----------------------------------------------
05    1:         38445        5597736  <constMethodKlass>
06    2:         38445        5237288  <methodKlass>
07    3:          3500        3749504  <constantPoolKlass>
08    4:         60858        3242600  <symbolKlass>
09    5:          3500        2715264  <instanceKlassKlass>
10    6:          2796        2131424  <constantPoolCacheKlass>
11    7:          5543        1317400  [I
12    8:         13714        1010768  [C
13    9:          4752        1003344  [B
14   10:          1225         639656  <methodDataKlass>
15   11:         14194         454208  java.lang.String
16   12:          3809         396136  java.lang.Class
17   13:          4979         311952  [S
18   14:          5598         287064  [[I
19   15:          3028         266464  java.lang.reflect.Method
20   16:           280         163520  <objArrayKlassKlass>
21   17:          4355         139360  java.util.HashMap$Entry
22   18:          1869         138568  [Ljava.util.HashMap$Entry;
23   19:          2443          97720  java.util.LinkedHashMap$Entry
24   20:          2072          82880  java.lang.ref.SoftReference
25   21:          1807          71528  [Ljava.lang.Object;
26   22:          2206          70592  java.lang.ref.WeakReference
27   23:           934          52304  java.util.LinkedHashMap
28   24:           871          48776  java.beans.MethodDescriptor
29   25:          1442          46144  java.util.concurrent.ConcurrentHashMap$HashEntry
30   26:           804          38592  java.util.HashMap
31   27:           948          37920  java.util.concurrent.ConcurrentHashMap$Segment
32   28:          1621          35696  [Ljava.lang.Class;
33   29:          1313          34880  [Ljava.lang.String;
34   30:          1396          33504  java.util.LinkedList$Entry
35   31:           462          33264  java.lang.reflect.Field
36   32:          1024          32768  java.util.Hashtable$Entry
37   33:           948          31440  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;

 

    class name是對象類型,說明如下:

1 B  byte
2 C  char
3 D  double
4 F  float
5 I  int
6 J  long
7 Z  boolean
8 [  數組,如[I表示int[]
9 [L+類名 其他對象

    還有一個很常用的情況是:用jmap把進程內存使用情況dump到文件中,再用jhat分析查看。jmap進行dump命令格式如下:

1 jmap -dump:format=b,file=dumpFileName

    我一樣地對上面進程ID為21711進行Dump:

1 root@ubuntu:/# jmap -dump:format=b,file=/tmp/dump.dat 21711     
2 Dumping heap to /tmp/dump.dat ...
3 Heap dump file created

   dump出來的文件可以用MAT、VisualVM等工具查看,這里用jhat查看:

01 root@ubuntu:/# jhat -port 9998 /tmp/dump.dat
02 Reading from /tmp/dump.dat...
03 Dump file created Tue Jan 28 17:46:14 CST 2014
04 Snapshot read, resolving...
05 Resolving 132207 objects...
06 Chasing references, expect 26 dots..........................
07 Eliminating duplicate references..........................
08 Snapshot resolved.
09 Started HTTP server on port 9998
10 Server is ready.

     然后就可以在瀏覽器中輸入主機地址:9998查看了:

JVM性能調優監控工具jps、jstack、jmap、jhat、jstat使用詳解

    上面紅線框出來的部分大家可以自己去摸索下,最后一項支持OQL(對象查詢語言)。

5、 jstack(Stack Trace for Java)

    jstack主要用來查看某個Java進程內的線程堆棧信息。語法格式如下:

1 jstack [option] pid
2 jstack [option] executable core
3 jstack [option] [server-id@]remote-hostname-or-ip

 

    命令行參數選項說明如下:

1 -l long listings,會打印出額外的鎖信息,在發生死鎖時可以用jstack -l pid來觀察鎖持有情況
2 -m mixed mode,不僅會輸出Java堆棧信息,還會輸出C/C++堆棧信息(比如Native方法)

    jstack可以定位到線程堆棧,根據堆棧信息我們可以定位到具體代碼,所以它在JVM性能調優中使用得非常多。下面我們來一個實例找出某個Java進程中最耗費CPU的Java線程並定位堆棧信息,用到的命令有ps、top、printf、jstack、grep。

    第一步先找出Java進程ID,我部署在服務器上的Java應用名稱為mrf-center:

1 root@ubuntu:/# ps -ef | grep mrf-center | grep -v grep
2 root     21711     1  1 14:47 pts/3    00:02:10 java -jar mrf-center.jar

 

    得到進程ID為21711,第二步找出該進程內最耗費CPU的線程,可以使用ps -Lfp pid或者ps -mp pid -o THREAD, tid, time或者top -Hp pid,我這里用第三個,輸出如下:

JVM性能調優監控工具jps、jstack、jmap、jhat、jstat使用詳解

    TIME列就是各個Java線程耗費的CPU時間,CPU時間最長的是線程ID為21742的線程,用

1 printf "%x\n" 21742

    得到21742的十六進制值為54ee,下面會用到。    

    OK,下一步終於輪到jstack上場了,它用來輸出進程21711的堆棧信息,然后根據線程ID的十六進制值grep,如下:

1 root@ubuntu:/# jstack 21711 | grep 54ee
2 "PollIntervalRetrySchedulerThread" prio=10 tid=0x00007f950043e000 nid=0x54ee in Object.wait() [0x00007f94c6eda000]

    可以看到CPU消耗在PollIntervalRetrySchedulerThread這個類的Object.wait(),我找了下我的代碼,定位到下面的代碼:

01 // Idle wait
02 getLog().info("Thread [" + getName() + "] is idle waiting...");
03 schedulerThreadState = PollTaskSchedulerThreadState.IdleWaiting;
04 long now = System.currentTimeMillis();
05 long waitTime = now + getIdleWaitTime();
06 long timeUntilContinue = waitTime - now;
07 synchronized(sigLock) {
08     try {
09         if(!halted.get()) {
10             sigLock.wait(timeUntilContinue);
11         }
12     
13     catch (InterruptedException ignore) {
14     }
15 }

    它是輪詢任務的空閑等待代碼,上面的sigLock.wait(timeUntilContinue)就對應了前面的Object.wait()。

 

其他JVM性能調優參考資料:

《Java虛擬機規范》

《Java Performance》

《Trouble Shooting Guide for JavaSE 6 with HotSpot VM》: http://www.oracle.com/technetwork/java/javase/tsg-vm-149989.pdf 

《Effective Java》

VisualVM: http://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/

jConsole: http://docs.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html

Monitoring and Managing JavaSE 6 Applications: http://www.oracle.com/technetwork/articles/javase/monitoring-141801.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM