JVM性能調優監控工具使用說明

工具

  • jps:查看所有的jvm進程,功能等同於ps -ef | grep java
  • jmap:監視進程運行中的jvm物理內存的佔用情況,或從core文件中獲得內存具體情況
  • jstat:可以用來監視VM內存內的各種堆和非堆的大小及其內存使用量
  • jstack:觀察jvm中當前所有線程的運行情況和線程當前狀態,或從core文件中獲得java stack和native stack的信息

用法

jps

> jps -help

usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

Definitions:
    <hostid>:      <hostname>[:<port>]
  • -q 只顯示pid
  • -l 輸出應用程序main class的完整package名
  • -m 輸出傳遞給main方法的參數
  • -v 輸出傳遞給JVM的參數

jmap

jmap -help

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

jmap -histo[:live] <pid>

打印當前java堆中各個對象的數量、大小。如果添加了live,只會打印活躍的對象

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:       1041541      146135656  [B ([email protected])
   2:        886075       21265800  java.lang.String ([email protected])
   3:        100542       19004544  [I ([email protected])
   4:        181568       14272328  [Ljava.lang.Object; ([email protected])
   5:         82416        9629864  java.lang.Class ([email protected])
   ...

jmap -dump:[live,]format=b,file=<filename> <pid>

通過-dump選項,把java堆中的對象dump到本地文件,然後使用MAT進行分析。 如果添加了live,只會dump活躍的對象。

jmap -heap <pid>

通過-heap選項,打印java堆的配置情況和使用情況,還有使用的GC算法。

jmap -finalizerinfo <pid>

通過-finalizerinfo選項,打印那些正在等待執行finalize方法的對象。

jmap -permstat <pid>

通過-permstat選項,打印java堆永久代的信息,包括class loader相關的信息,和interned Strings的信息。

jstat

jstat有如下參數:

-class:統計class loader行爲信息 
-compile:統計編譯行爲信息 
-gc:統計jdk gc時heap信息,容量,使用大小
-gccapacity:統計不同的generations(包括新生區,老年區,permanent區)相應的heap容量情況 
-gccause:統計gc的情況,(同-gcutil)和引起gc的事件 
-gcnew:統計gc時,新生代的情況 
-gcnewcapacity:統計gc時,新生代heap容量 
-gcold:統計gc時,老年區的情況 
-gcoldcapacity:統計gc時,老年區heap容量 
-gcpermcapacity:統計gc時,permanent區heap容量 
-gcutil:統計gc時,heap情況,使用百分比,次數,時間

其中比較常用的有:-gc,-gcutil,-gc

用法:jstat -options <pid> [<interval>] [<count>]

比如:jstat -gc 1860 2000 5

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
28608.0 28608.0 4848.7  0.0   228928.0 207769.8  571996.0   331857.0  517424.0 492050.8 76272.0 65981.8    350    4.946  15     35.089   40.035
28608.0 28608.0 4848.7  0.0   228928.0 211182.3  571996.0   331857.0  517424.0 492050.8 76272.0 65981.8    350    4.946  15     35.089   40.035
28608.0 28608.0 4848.7  0.0   228928.0 211621.0  571996.0   331857.0  517424.0 492050.8 76272.0 65981.8    350    4.946  15     35.089   40.035
28608.0 28608.0 4848.7  0.0   228928.0 211621.0  571996.0   331857.0  517424.0 492050.8 76272.0 65981.8    350    4.946  15     35.089   40.035
28608.0 28608.0 4848.7  0.0   228928.0 211912.6  571996.0   331857.0  517424.0 492050.8 76272.0 65981.8    350    4.946  15     35.089   40.035

C => capacity, U => used, T => time

在比如:jstat -gcutil 1860 2000 5

  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00  11.50  61.95  58.02  95.10  86.51    351    4.956    15   35.089   40.045
  0.00  11.50  63.17  58.02  95.10  86.51    351    4.956    15   35.089   40.045
  0.00  11.50  63.17  58.02  95.10  86.51    351    4.956    15   35.089   40.045
  0.00  11.50  63.77  58.02  95.10  86.51    351    4.956    15   35.089   40.045
  0.00  11.50  64.67  58.02  95.10  86.51    351    4.956    15   35.089   40.045

S0,S1,E,O,M顯示的都是使用佔比,YGC和FGC是次數,後面帶T的表示時間,單位秒

jstack

jstack用於生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。

線程的物種狀態

  • 新建狀態(New) 新創建了一個線程對象。
  • 就緒狀態(Runnable) 線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。
  • 運行狀態(Running) 就緒狀態的線程獲取了CPU,執行程序代碼。
  • 阻塞狀態(Blocked) 阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。
  • 死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

其中,阻塞狀態又分三種情況:

  • 等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
  • 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。
  • 其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

在dump中,線程一般存在如下幾種狀態:

  • RUNNABLE,線程運行中或I/O等待
  • BLOCKED,線程被阻塞,在等待monitor鎖(synchronized關鍵字)
  • TIMED_WAITING 線程在等待喚醒,但設置了時限
  • WAITING 線程在無限等待喚醒

調用修飾

表示線程在方法調用時,額外的重要的操作。線程Dump分析的重要信息。修飾上方的方法調用。

  • locked <地址> 目標:使用synchronized申請對象鎖成功,監視器的擁有者。
  • waiting to lock <地址> 目標:使用synchronized申請對象鎖未成功,在進入區等待。在調用棧頂出現,線程狀態爲Blocked。
  • waiting on <地址> 目標:使用synchronized申請對象鎖成功後,調用了wait方法,進入對象的等待區等待。在調用棧頂出現,線程狀態爲WAITING或TIMED_WATING。
  • parking to wait for <地址> 目標

線程動作

線程狀態產生的原因

  • runnable:狀態一般爲RUNNABLE。
  • in Object.wait():等待區等待,狀態爲WAITING或TIMED_WAITING。
  • waiting for monitor entry:進入區等待,狀態爲BLOCKED。
  • waiting on condition:等待區等待、被park。
  • sleeping:休眠的線程,調用了Thread.sleep()。

一些常見的異常情況

  1. 進入區等待,線程狀態BLOCKED,線程動作wait on monitor entry,調用修飾waiting to lock總是一起出現。 這種情況說明存在鎖競爭,應該儘量避免這種情況發生

  2. 同步塊阻塞,一個線程鎖住某對象,大量其他線程在該對象上等待。

    	"blocker" runnable
    	java.lang.Thread.State: RUNNABLE
    	at com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)
    	- locked <0x00000000eb8eff68> (a java.lang.Object)
    
    	"blockee-11" waiting for monitor entry
    	java.lang.Thread.State: BLOCKED (on object monitor)
    	at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
    	- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
    
    	"blockee-86" waiting for monitor entry
    	java.lang.Thread.State: BLOCKED (on object monitor)
    	at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
    	- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
    
  3. 持續運行的IO IO操作是可以以RUNNABLE狀態達成阻塞。例如:數據庫死鎖、網絡讀寫。 格外注意對IO線程的真實狀態的分析。 一般來說,被捕捉到RUNNABLE的IO調用,都是有問題的。

找出最耗CPU的線程

步驟如下:

  1. 首先用top命令找出佔用cpu最多的jvm進程id
  2. 首先使用top -Hp <pid> 找出佔用CPU最多的線程id
  3. 使用printf "%x\n" <threadid>得到線程id的十六進制
  4. 使用jstack <pid>得到進程堆棧信息
  5. 使用3中得到的十六進制作爲關鍵字進行查找,定位到線程,得到其堆棧信息

查找死鎖

給出一個死鎖的線程狀態例子:


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f0134003ae8 (object 0x00000007d6aa2c98, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f0134006168 (object 0x00000007d6aa2ca8, a java.lang.Object),
  which is held by "Thread-1"
 
Java stack information for the threads listed above:
===================================================
"Thread-1":
    at javaCommand.DeadLockclass.run(JStackDemo.java:40)
    - waiting to lock <0x00000007d6aa2c98> (a java.lang.Object)
    - locked <0x00000007d6aa2ca8> (a java.lang.Object)
    at java.lang.Thread.run(Thread.java:745)
"Thread-0":
    at javaCommand.DeadLockclass.run(JStackDemo.java:27)
    - waiting to lock <0x00000007d6aa2ca8> (a java.lang.Object)
    - locked <0x00000007d6aa2c98> (a java.lang.Object)
    at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

jstack可以幫助我們發現診斷一些死鎖情況。

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