虛擬機性能監控與故障處理工具

  JDK自帶的工具能夠方便的幫助咱們處理一些問題,包括查看JVM參數,分析內存變化,查看內存區域,查看線程等信息。html

  咱們熟悉的有java.exe,javac.exe,javap.exe(偶爾用),jps.exe,jmap.exe....等,下面會詳細介紹。分別在linux和windows下面介紹。windows與linux下面使用的都是JDK1.7.0_80java

windows下的jdk版本以下:linux

$ java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

 

linux下的JDK版本以下:spring

[root@VM_0_12_centos ~]# java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

 

1.工具簡介

1.1windows下面查看

$ dir
appletviewer.exe    java-rmi.exe  jrunscript.exe    pack200.exe
apt.exe             javaw.exe     jsadebugd.exe     policytool.exe
extcheck.exe        javaws.exe    jstack.exe        rmic.exe
idlj.exe            jcmd.exe      jstat.exe         rmid.exe
jabswitch.exe       jconsole.exe  jstatd.exe        rmiregistry.exe
jar.exe             jdb.exe       jvisualvm.exe     schemagen.exe
jarsigner.exe       jhat.exe      keytool.exe       serialver.exe
java.exe            jinfo.exe     kinit.exe         servertool.exe
javac.exe           jli.dll       klist.exe         tnameserv.exe
javadoc.exe         jmap.exe      ktab.exe          unpack200.exe
javafxpackager.exe  jmc.exe       msvcr100.dll      wsgen.exe
javah.exe           jmc.ini       native2ascii.exe  wsimport.exe
javap.exe           jps.exe       orbd.exe          xjc.exe

 

2.linux下面查看工具

[root@VM_0_12_centos bin]# ls
appletviewer  javac           jconsole  jps         native2ascii  serialver
apt           javadoc         jcontrol  jrunscript  orbd          servertool
ControlPanel  javafxpackager  jdb       jsadebugd   pack200       tnameserv
extcheck      javah           jhat      jstack      policytool    unpack200
idlj          javap           jinfo     jstat       rmic          wsgen
jar           java-rmi.cgi    jmap      jstatd      rmid          wsimport
jarsigner     javaws          jmc       jvisualvm   rmiregistry   xjc
java          jcmd            jmc.ini   keytool     schemagen

 

其實上面的工具大部分都是jdk/lib/tools.jar 類庫的一層包裝而已,它們主要的功能代碼是在tools.jar中實現的。apache

 

2.常見工具簡要使用

研究常見的工具的使用。主要有下面工具:windows

jps---JVM Process Status tool,顯示指定系統內全部的HotSpot虛擬機進程centos

jstat---JVM Statistics Monitoring tool,用於收集HotSpot虛擬機各方面的運行數據數組

jinfo---Configuration Info For Java,顯示虛擬機配置信息瀏覽器

jmap---Memory Map for Java,生成虛擬機的內存轉儲快照(heapdump文件)tomcat

jhat---JVM Heap Dump Browser,用於分析heapdump文件,它會創建一個http/html服務器。讓用於能夠在瀏覽器上查看分析結果

jstack---Stack Trace For Java,顯示虛擬機的線程快照。

 

下面研究具體的使用。

2.1   jps---虛擬機進程情況工具(經常使用)

   JDK的不少工具都參考了UNIX系統的命名方式,jps(JVM Process Status)是其中的典型。除了名字像JVM的ps以外,功能也和UNIX的ps差很少:能夠列出正在運行的虛擬機進程,並顯示虛擬機執行主類(Main Class,main函數所在的類)名稱以及這些進程的本地虛擬機惟一ID(Local  Vitural Machine Identifier,LVMID)。雖然功能比較單一,但它是使用最頻繁的JDK命令行工具,由於其餘工具的使用都須要依賴此工具識別處的LVMID做爲輸入。對於本地虛擬機進程來講,LVMID與操做系統的進行ID(Process Identifier,PID)是一致的,使用windows的任務管理器或者unix的ps命令也能夠查詢到虛擬機的LVMID,可是若是啓動了多個虛擬機進程,沒法根據名稱定位時,那就只能根據jps來區分了。

jps命令格式:

jps [option] [hostid]

 

例如:

C:\Users\Administrator>jps -l
2204 org.apache.catalina.startup.Bootstrap
2084
6596 sun.tools.jps.Jps

  jps能夠經過RMI協議查詢開啓了RMI服務的遠程虛擬機進程狀態,hostid爲RMI註冊表中註冊的主機名。jps的常見其餘選項以下:

-q   只輸出LVMID,省略主類的名稱

-m   輸出虛擬機進程啓動時傳遞給主類main函數的參數

-l  輸出主類的全名,若是進程執行的是jar包,輸出Jar路徑

-v  輸出虛擬機進程啓動時JVM參數

 

本身在windows下面的測試:

C:\Users\Administrator>jps -m -l -v
9772 sun.tools.jps.Jps -m -l -v -Denv.class.path=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar -Dapplication.home=C:\Program Files\Java\jdk1.7.0_80 -Xms8m
2204 org.apache.catalina.startup.Bootstrap start -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:53302 -Dcatalina.base=E:\xiangmu\.metadata\.plugins\org.eclipse.wst.server.core\tmp1 -Dcatalina.home=E:\tomcat\apache-tomcat-7.0.88 -Dwtp.deploy=E:\xiangmu -Djava.endorsed.dirs=E:\tomcat\apache-tomcat-7.0.88\endorsed -Dfile.encoding=UTF-8
2084  -Dosgi.requiredJavaVersion=1.7 -Xms256m -Xmx1024m -Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Dsun.lang.ClassLoader.allowArraySyntax=true -XX:MaxPermSize=256m

 

9772是jps命令自身,可見jps自己也是一個Java程序,其主類位於sun.tools.jps.Jps(該類位於jdk/lib/tools.jar)

2204 是本身啓動的一個tomcat程序,主類是Bootstrap,向main函數傳遞的參數是start,後面是一些設置

2084是Eclipse的PID,後面跟着eclipse.ini中對eclipse的設置(這也能夠用來判斷eclipse啓動參數)

 

本身在linux下面的測試

[root@VM_0_12_centos ~]# jps -m -l -v
4211 sun.tools.jps.Jps -m -l -v -Denv.class.path=.:/opt/java/jdk1.7.0_80/lib:/opt/java/jdk1.7.0_80/jre/lib -Dapplication.home=/opt/java/jdk1.7.0_80 -Xms8m
21991 org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/opt/apache-tomcat/apache-tomcat-7.0.72/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.endorsed.dirs=/opt/apache-tomcat/apache-tomcat-7.0.72/endorsed -Dcatalina.base=/opt/apache-tomcat/apache-tomcat-7.0.72 -Dcatalina.home=/opt/apache-tomcat/apache-tomcat-7.0.72 -Djava.io.tmpdir=/opt/apache-tomcat/apache-tomcat-7.0.72/temp

 

4211 是jps命令自身的pid,

21991 是linux下面的一個tomcat的pid,其主類、傳遞的參數以及配置與上面相似。

 

2.2 jstat:虛擬機統計信息監視工具

  jstat(JVM Statistics Monitoring Machine)是用於監視虛擬機各類運行狀態信息的工具。它能夠顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、JID編譯等運行時數據,在沒有GUI的服務器上,對於定位虛擬機性能問題很是重要。

  jstat命令格式爲:

jstat [   option    vmid    [interval   [s|ms]   [count]   ]     ]

 

 對於命令格式中的VMID與LVMID須要特別說明一下:若是是本地虛擬機進程,VMID與LVMID是一致的,若是是遠程虛擬機進程,那VMID的格式應當以下:

[protocol:][//]lvmid[@hostname[:port]/servername]

 

 

  參數interval和count表明查詢的間隔和次數,若是省略這兩個參數,說明只查詢一次。假設須要每250ms查詢一次pid爲 2204 程序的垃圾收集狀態,總共查詢20次,那麼命令應當是:

jstat -gc 2204 250 20

 

  

  選項option表明着用戶但願查詢的虛擬機信息,分爲3類:類裝載、垃圾收集、運行期編譯情況,具體做用以下:

-class  監視類裝載、卸載數量、總空間以及類裝載所耗費的時間

-gc  監視java堆情況,包括Eden區、兩個suprivor區、老年代、永久代等的容量、已用空間、GC時間合計等信息(實際在hotspot虛擬機中,永久代不屬於堆內存,並且在JDK8已經被元空間MetaSpace所取代)

-gccapacity  監視內容與-gc基本相同,可是輸出主要關注的是Java堆各個區域使用的最大、最小空間

-gcutil  監視內容與-gc基本相同,但輸出主要關注已使用空間佔總空間的比例

-gccause  監視內容與-gc基本相同,可是會額外輸出致使上一致gc的緣由。

-gcnew  監視新生代GC情況

-gcnewcapacity  監視內容與-gcnew基本相同,可是輸出主要關注的是使用的最大、最小空間

-gcold  監視老年代GC情況

-gcoldcapacity  監視內容與-gcold基本相同,可是輸出主要關注的是使用的最大、最小空間

-gcpermcapacity  輸出永久代最大、最小空間

-compiler  輸出JIT編譯過的方法、耗時等信息

-printcompilation  輸出已經被JIT編譯的方法

 

例如本身windows下面的測試

 

C:\Users\Administrator>jstat -gcutil 2204
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  33.52  54.34  55.99     15    0.322     1    0.282    0.604

 

  實驗結果代表:這臺虛擬機的新生代Enen區(E表示Eden)使用了33.52%空間,兩個Suprivor區(SO和S1)都是空的,老年代Old(o表示Old)佔用了54.34%,永久代(Perm)佔用了55.99%。程序運行以愛共發生Minor GC(YGC表示Young GC)15次,總耗時0.322秒。發生FullGC(FGC)1次,FullGC總耗時0.282秒,全部GC耗時(GCT) 0.604秒。

 

下面是一些其餘測試:

C:\Users\Administrator>jstat -gccause 2204
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
  0.00   0.00  33.52  54.34  55.99     15    0.322     1    0.282    0.604 Allocation Failure   No GC

C:\Users\Administrator>jstat -class 2204
Loaded  Bytes  Unloaded  Bytes     Time
  7880 15537.3        0     0.0      13.07

C:\Users\Administrator>jstat -gc 2204
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
43520.0 44544.0  0.0    0.0   499200.0 172323.0  120320.0   65384.2   83456.0 46728.6     15    0.322   1      0.282    0.604

C:\Users\Administrator>jstat -compiler 2204
Compiled Failed Invalid   Time   FailedType FailedMethod
    1504      1       0    17.19          1 org/apache/catalina/loader/WebappClassLoaderBase findResourceInternal

C:\Users\Administrator>jstat -gccapacity 2204
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC      PGCMN    PGCMX     PGC       PC     YGC    FGC
 43008.0 686080.0 684544.0 43520.0 44544.0 499200.0    85504.0  1372160.0   120320.0   120320.0  21504.0  83968.0  83456.0  83456.0     15     1

 

本身在linux下面的測試:

[root@VM_0_12_centos ~]# jstat -gcutil 21991
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00   9.81  36.74  64.58  99.78    931    8.255    17    2.609   10.865
[root@VM_0_12_centos ~]# jstat -gccause 21991
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
  0.00   9.81  38.73  64.58  99.78    931    8.255    17    2.609   10.865 Allocation Failure   No GC
[root@VM_0_12_centos ~]# jstat -class 21991
Loaded  Bytes  Unloaded  Bytes     Time
 12028 24837.3       48    70.8      66.16
[root@VM_0_12_centos ~]# jstat -compiler 21991
Compiled Failed Invalid   Time   FailedType FailedMethod
    2543      2       0    71.49          1 org/apache/jasper/xmlparser/ParserUtils convert

 

一些具體的參數解釋以下:

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 : 最大持有次數限制

 

2.3 jinfo  Java配置信息工具

  jinfo(Configuration Info for Java)的做用是 實時查看和調整虛擬機的各項參數。使用jps -v 能夠查看虛擬器啓動時候顯示指定的參數列表,可是若是想查找未被顯示指定的參數的系統的默認值,除了去查閱資料,就只能使用jinfo的 -flag選項進行查詢了(若是隻限於JDK版本6以上,使用Java -XX:+PrintFlagsFinal 查看默認值也是一個很好的選擇),jinfo還可使用 -sysprops選項把虛擬機進程的System.getProperties()的內容打印出來,JDK1.6以後,jinfo在windows和linux平臺都提供,而且加入了運行期修改參數的能力,可使用 -flag [+|-] name 或者 -flag name=value修改一部分運行期可寫的虛擬機參數值。JDK1.6中,jinfo對於windows平臺功能仍然有較大限制,只提供了最基本的-flag選項。

jinfo的命令格式以下:

C:\Users\Administrator>jinfo -flag
Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

例如:

(1)查看一個JVM的配置信息,也就是System.getProperties能夠獲取的信息:

C:\Users\Administrator>jinfo 2084
Attaching to process ID 2084, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11
Java System Properties:

java.vendor = Oracle Corporation
osgi.bundles.defaultStartLevel = 4
org.eclipse.debug.ui.breakpoints.toggleFactoriesUsed = false
org.osgi.supports.framework.extension = true
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
eclipse.p2.profile = epp.package.jee
os.name = Windows 8
...

 

 或者

C:\Users\Administrator>jinfo -sysprops 2084
Attaching to process ID 2084, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11
java.vendor = Oracle Corporation
osgi.bundles.defaultStartLevel = 4
org.eclipse.debug.ui.breakpoints.toggleFactoriesUsed = false
org.osgi.supports.framework.extension = true
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
eclipse.p2.profile = epp.package.jee
os.name = Windows 8
...

 

(2)查看JVM的啓動參數信息

C:\Users\Administrator>jinfo -flags 2084
Attaching to process ID 2084, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11

-Dosgi.requiredJavaVersion=1.7 -Xms256m -Xmx1024m -Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Dsun.lang.ClassLoader.allowArraySyntax=true -XX:MaxPermSize=256m

 

(3)查看是否開啓了GC日誌的打印:

C:\Users\Administrator>jinfo -flag PrintGC 2084
-XX:+PrintGC

C:\Users\Administrator>jinfo -flag PrintGCDetails 2084
-XX:+PrintGCDetails

 

(4)關閉GC打印功能

C:\Users\Administrator>jinfo -flag -PrintGC 2084

C:\Users\Administrator>jinfo -flag -PrintGCDetails 2084

C:\Users\Administrator>jinfo -flag PrintGC 2084
-XX:-PrintGC

C:\Users\Administrator>jinfo -flag PrintGCDetails 2084
-XX:-PrintGCDetails

 

有一點必須注意:PrintGC必須開啓,只開啓PrintGCDetails、PrintGCTimeStamps不會輸出GC,必須PrintGC同時開啓

 

(5)查看啓動參數

C:\Users\Administrator>jinfo -flag MaxHeapSize 2084
-XX:MaxHeapSize=1073741824

C:\Users\Administrator>jinfo -flag PermSize 2084
-XX:PermSize=21757952

 

2.4  jmap  Java內存映像工具  (經常使用)

  jmap(Memory map for java)命令用於生成堆轉儲快照(通常稱爲heapdump或dump文件),若是不使用jmap命令,想要獲取java堆轉儲快照,還有一些比較暴力的手段:好比說用 -XX:+HeapDumpOnOutOfMemoryError參數,可讓虛擬機在發生OOM異常以後自動生成dump文件,經過 -XX:+HeapDumpOnCtrlBreak參數則可使用 [Ctrl]+[Break]鍵讓虛擬機生成dump文件,又或者在linux下面經過  Kill -3 命令發送進程退出信號"嚇唬"一下虛擬機,也能拿到dump文件。

  jmap的做用不只是爲了獲取dump文件,它還能夠查詢finalize執行隊列、Java堆和永久代的詳細信息,如空間使用率、當前使用的是哪一種收集器等。

  和jinfo同樣,jmap有很多功能在windows平臺下都是受限的,除了生成dump文件的-dump選項和用於查看每一個類的實例、空間佔用的-histo選項在全部操做系統都提供以外,其他選項都只能在Linux/Solaris下使用。

  jmap的命令格式:

C:\Users\Administrator>jmap
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
    -permstat            to print permanent generation 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

 

(1)windows下面的測試

  • 查看JVM的內存信息:
C:\Users\Administrator>jmap -heap 2084  #查看堆內存
Attaching to process ID 2084, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 0
   MaxHeapFreeRatio = 100
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 268435456 (256.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 295698432 (282.0MB)
   used     = 12552440 (11.970939636230469MB)
   free     = 283145992 (270.02906036376953MB)
   4.245014055400875% used
From Space:
   capacity = 13107200 (12.5MB)
   used     = 13080592 (12.474624633789062MB)
   free     = 26608 (0.0253753662109375MB)
   99.7969970703125% used
To Space:
   capacity = 10485760 (10.0MB)
   used     = 0 (0.0MB)
   free     = 10485760 (10.0MB)
   0.0% used
PS Old Generation
   capacity = 541589504 (516.5MB)
   used     = 211967328 (202.14779663085938MB)
   free     = 329622176 (314.3522033691406MB)
   39.13800515602311% used
PS Perm Generation
   capacity = 166199296 (158.5MB)
   used     = 166122072 (158.42635345458984MB)
   free     = 77224 (0.07364654541015625MB)
   99.95353530258035% used

44318 interned Strings occupying 4390312 bytes.

 

  • 查看一個JVM中的存活(加上 :live 會只顯示存活對象)對象:(我將信息重定向到一個文件中了,存活對象太多,篇幅太長)
C:\Users\Administrator>jmap -histo:live 2084 >> tt.txt

 

打開文件查看結果:最後有3068個類   (咱們也能夠發現pring的單例模式,確實使用了CGLIB代理模式生成惟一的代理對象)

 

 

 

  •  生成Java堆轉儲快照文件(對文件的分析在後面介紹)
C:\Users\Administrator>jmap -dump:live,format=b,file=heap.bin  2204
Dumping heap to C:\Users\Administrator\heap.bin ...
Heap dump file created

 

(2)linux下面的測試:

[root@VM_0_12_centos ~]# jmap -heap 21991  #查看內存JVM參數
Attaching to process ID 21991, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 482344960 (460.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 78053376 (74.4375MB)
   used     = 12557464 (11.975730895996094MB)
   free     = 65495912 (62.461769104003906MB)
   16.088303470691645% used
Eden Space:
   capacity = 69402624 (66.1875MB)
   used     = 11711400 (11.168861389160156MB)
   free     = 57691224 (55.018638610839844MB)
   16.874578113934135% used
From Space:
   capacity = 8650752 (8.25MB)
   used     = 846064 (0.8068695068359375MB)
   free     = 7804688 (7.4431304931640625MB)
   9.780236446496213% used
To Space:
   capacity = 8650752 (8.25MB)
   used     = 0 (0.0MB)
   free     = 8650752 (8.25MB)
   0.0% used
tenured generation:
   capacity = 173322240 (165.29296875MB)
   used     = 111934032 (106.74861145019531MB)
   free     = 61388208 (58.54435729980469MB)
   64.58145936724566% used
Perm Generation:
   capacity = 77332480 (73.75MB)
   used     = 77163160 (73.5885238647461MB)
   free     = 169320 (0.16147613525390625MB)
   99.78104930813029% used

30082 interned Strings occupying 3502776 bytes.
[root@VM_0_12_centos ~]# jmap -histo:live 21991 >> exam.info  #查看存活對象信息,重定向到文件中
[root@VM_0_12_centos ~]# jmap -dump:live,format=b,file=heap.bin  21991  #查看堆轉儲快照信息
Dumping heap to /root/heap.bin ...
Heap dump file created

 

2.5 jhat 虛擬機堆轉儲快照分析工具

  jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析jmap生成的堆轉儲快照。jhat內置了一個小型的HTTP/HTML分析器,生成dump文件的分析結果以後,能夠在瀏覽器查看。通常也用不到這個工具,主要緣由有:一是通常不會在部署應用程序的服務器上直接生成dump文件,即便這樣作了,也會盡可能將dump文件複製到其餘機器進行分析,由於分析工做是一個耗時且消耗硬件資源的工做;二是此工具的分析功能相對比較簡陋,比較好的有Visual VM,以及專業用於分析dump文件的Eclipse Memory Analyzer等。

  接下來用jhat分析上面jmap生成的dump快照信息:

C:\Users\Administrator>jhat heap.bin
Reading from heap.bin...
Dump file created Mon Dec 03 15:04:02 CST 2018
Snapshot read, resolving...
Resolving 816411 objects...
Chasing references, expect 163 dots...................................................................................................................................................................
Eliminating duplicate references...................................................................................................................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

 

  能夠看到上面在端口7000,創建了http監聽,輸入http://localhost:7000/ 便可查看分析信息

 

 

   

  分析結果默認是以包爲單位分組顯示,分析內存泄漏問題主要會使用其中的"heap histogram"(與jmap -histo)功能同樣能夠查看類的統計信息與OQL頁籤的功能,前者能夠找到內存中總容量最大的對象,後者是標準的對象查詢語言,使用相似SQL的語法對內存中的對象進行查詢統計(這個參考深刻JVM虛擬機一書能夠方便使用)。

 

查看heap histogram

 

點一個類進去再次查看:(這裏也解釋了spring的cglib代理功能基於繼承方式實現)

 

 再次點擊DepartmentServiceImpl查看:

 

 

 2.6 jstack  java堆棧跟蹤工具

  jstack(Stack Trace For Java)命令用於生成虛擬機當前時刻的線程快照(通常成爲threaddump或者javacore文件)。線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的目的是爲了定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源等待的時間過長等緣由。線程出現停頓的時候能夠經過jstack來查看各個線程的調用堆棧,就能夠知道響應的線程到底在後臺作什麼或者等待什麼資源。

  jstack的命令格式以下:

C:\Users\Administrator>jstack
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

 

例如:

C:\Users\Administrator>jstack -l 2204

 

  在JDK1.5中,java.lang.Thread類新增了一個getAllStackTraces()方法用於獲取虛擬機中全部線程的StackTraceElement對象。使用這個對象能夠經過簡單的幾行代碼就完成jstack的大部分功能,在實際項目中能夠用這個作管理界面,可隨時查看線程堆棧。以下代碼:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.Map"%>
<html>
<head>
<title>服務器線程堆棧信息</title>
</head>
<body>
    <%
        for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
            Thread thread = (Thread) stackTrace.getKey();
            StackTraceElement[] stackTraceElements = stackTrace.getValue();
            if (thread.equals(Thread.currentThread())) {
                continue;
            }
            out.print("\n 線程->" + thread.getName() + "\n");
            for (StackTraceElement s : stackTraceElements) {
                out.print("\t" + s + "\n");
            }
        }
    %>
</body>
</html>

 

補充:jstack也能夠用來檢測死鎖

一個死鎖程序:

package cn.xm.exam.test;

public class test {

    public static void main(String[] args) {
        final Integer lock1 = 1;
        final Integer lock2 = 2;
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock1) {
                    try {
                        System.out.println("lock1-----");
                        Thread.sleep(2 * 1000);
                        synchronized (lock2) {
                            System.out.println("lock1-----lock2");
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        new Thread(r1).start();

        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock2) {
                    try {
                        System.out.println("lock2-----");
                        Thread.sleep(2 * 1000);
                        synchronized (lock1) {
                            System.out.println("lock2-----lock1");
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        new Thread(r2).start();
    }
}

 

jps+jstack檢測死鎖:

C:\Users\liqiang>jps
336164
342476 test
343376 Jps

C:\Users\liqiang>jstack 342476
2018-12-10 21:19:47
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.80-b11 mixed mode):

"DestroyJavaVM" prio=6 tid=0x00000000026a5800 nid=0x53a04 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" prio=6 tid=0x000000000e97e800 nid=0x53134 waiting for monitor entry [0x000000000ef7f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.xm.exam.test.test$2.run(test.java:33)
        - waiting to lock <0x00000007c0af8478> (a java.lang.Integer)
        - locked <0x00000007c0af8488> (a java.lang.Integer)
        at java.lang.Thread.run(Thread.java:745)

"Thread-0" prio=6 tid=0x000000000e97d800 nid=0x53b40 waiting for monitor entry [0x000000000ee7f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at cn.xm.exam.test.test$1.run(test.java:16)
        - waiting to lock <0x00000007c0af8488> (a java.lang.Integer)
        - locked <0x00000007c0af8478> (a java.lang.Integer)
        at java.lang.Thread.run(Thread.java:745)

"Service Thread" daemon prio=6 tid=0x000000000d04b000 nid=0x4e590 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x000000000d03a000 nid=0x53594 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x000000000d035000 nid=0x52bd8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" daemon prio=10 tid=0x000000000d034000 nid=0x52d64 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x000000000d030800 nid=0x531f0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=8 tid=0x000000000278c800 nid=0x53868 in Object.wait() [0x000000000e37f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007c0904858> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
        - locked <0x00000007c0904858> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" daemon prio=10 tid=0x0000000002784800 nid=0x53adc in Object.wait() [0x000000000e27f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007c0904470> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:503)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
        - locked <0x00000007c0904470> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=10 tid=0x000000000cff3000 nid=0x53ad0 runnable

"GC task thread#0 (ParallelGC)" prio=6 tid=0x00000000026bb000 nid=0x53b08 runnable

"GC task thread#1 (ParallelGC)" prio=6 tid=0x00000000026bc800 nid=0x53ac4 runnable

"GC task thread#2 (ParallelGC)" prio=6 tid=0x00000000026be800 nid=0x53368 runnable

"GC task thread#3 (ParallelGC)" prio=6 tid=0x00000000026c0000 nid=0x53acc runnable

"VM Periodic Task Thread" prio=10 tid=0x000000000d08e800 nid=0x539a4 waiting on condition

JNI global references: 111


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000000278c3b8 (object 0x00000007c0af8478, a java.lang.Integer),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000000278af18 (object 0x00000007c0af8488, a java.lang.Integer),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at cn.xm.exam.test.test$2.run(test.java:33)
        - waiting to lock <0x00000007c0af8478> (a java.lang.Integer)
        - locked <0x00000007c0af8488> (a java.lang.Integer)
        at java.lang.Thread.run(Thread.java:745)
"Thread-0":
        at cn.xm.exam.test.test$1.run(test.java:16)
        - waiting to lock <0x00000007c0af8488> (a java.lang.Integer)
        - locked <0x00000007c0af8478> (a java.lang.Integer)
        at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

 

 3.JDK的可視化工具的使用 (只在windows下進行測試)

   JDK除了上面的大量的命令行工具,還提供了兩個可視化工具,JConsole和Visual VM。JConsole是JDL5纔有的,而Visual VM是JDK6的Update7中首次發佈的,如今已經成爲主流的多合一故障處理工具。

3.1Jconsole   java監控與管理控制檯

  JConsole(Java Monitoring and management Console)是基於JMX的可視化監視、管理工具。它管理部分的功能是針對JMX MBean進行管理,因爲MBean可使用代碼、中間件服務器的管理控制檯或者全部符合JMX規範的軟件進行訪問。

1.啓動JConsole

  雙擊Jdk/bin/jconsole.exe啓動便可。啓動以後會自動顯示本地的LVMID,至關於JPS功能,雙擊一個打開便可。

  也可使用下面的遠程進程進行遠程鏈接。

2.啓動以後查看結果:

 

   概述頁籤顯示的是整個虛擬機運行時數據的概覽,其中包括"堆使用狀況"、"線程"、"類"、"CPU佔用率等",這些曲線圖是後面內存、線程、類頁籤的彙總。

 

3.內存監控

   內存頁籤至關於可視化的jstat命令。(在這裏也能夠看出永久代屬於非堆區)。也能夠看到垃圾收集器以及垃圾收集時間等信息。

 

4.線程監控 

  線程頁籤至關於jstack命令,遇到線程停頓時能夠用這個命令進行分析。也能夠進行死鎖檢測。

  

 

 

  下面模擬一個線程死鎖的線程檢測:(一個簡單的雙同步發生的線程死鎖的例子)

package cn.xm.exam.test;

public class test {

    public static void main(String[] args) {
        final Integer lock1 = 1;
        final Integer lock2 = 2;
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock1) {
                    try {
                        System.out.println("lock1-----");
                        Thread.sleep(2 * 1000);
                        synchronized (lock2) {
                            System.out.println("lock1-----lock2");
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        new Thread(r1).start();

        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                synchronized (lock2) {
                    try {
                        System.out.println("lock2-----");
                        Thread.sleep(2 * 1000);
                        synchronized (lock1) {
                            System.out.println("lock2-----lock1");
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        new Thread(r2).start();
    }
}

 

再次檢測死鎖查看線程死鎖信息:(能夠看到多處一個死鎖籤,能夠看到發生死鎖的當前線程所擁有的鎖以及等待的另外一個鎖被另外一個線程鎖所擁有)

 

3.2  jvisualvm.exe  :多合一故障處理工具(重要 )

1.啓動

  位於jdk/bin/jvisualvm.exe,雙擊便可啓動,啓動也是會顯示JPS相似的功能,雙擊一個PID進去便可。

 

 

2.概述

  能夠看到參數信息,以及一些配置信息。有jps -ml的功能。其系統屬性的頁籤相似於jinfo功能。也能夠看到JVM參數的設置信息。

 

 

 3.監視頁籤查看概述信息

  也能夠dump出堆轉儲快照。

 

執行堆dump以後能夠進行分析類以及實例信息,相似於jmap -histo的功能 (重要) 。並且堆dump也能夠另存爲文件。

 

 

 

4.利用JVisualVM分析死鎖:(例子仍是上面的死鎖程序)

  會自動檢測死鎖。

 

 咱們dump出快照查看信息:

 

總結:  至此完成了工具的研究,最經常使用的是jps -lm   + jmap -heap pid   +    jmap -histo:live    。若是可使用可視化工具的話jvisualVM是不錯的選擇。

                                  

補充一些JVM中的知識,便於分析類以及其實例:

在Java中,任何類型都有class,包括基本數據類型與void。在Java中 [   表明數組,  [[  表明二維數組。依次類推。

其餘的描述標識符以下:

B---基本數據類型byte

C---基本數據類型char

D---基本數據類型double

F---基本數據類型float

I---基本數據類型int

J---基本數據類型long

S---基本數據類型short

Z---基本數據類型boolean

V---特殊類型void

L---對象類型(也就是引用類型)。例如 Ljava/lang/Object.

 

須要注意的是八種基本數據類型也有  calss,並且其對應包裝類型有一個TYPE成員變量就是其基本數據類型。特殊類型void也有class。基本類型的數組與引用類型數組也都有class

public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

 

 

例如:

     System.out.println(int.class);
        System.out.println(Integer.class);
        System.out.println(Integer.TYPE);

        System.out.println(Integer[].class);
        System.out.println(int[].class);
        System.out.println(void.class);

 

 結果:

int
class java.lang.Integer
int
class [Ljava.lang.Integer;
class [I
void

 注意int不是對象,因此沒有getClass方法。只有int.class

相關文章
相關標籤/搜索