tags: java, troubleshooting, monitor,jvmhtml
一句話歸納:原來jdk自帶的命令行工具如此好用,本文將詳細介紹。java
監測java應用,最方便的就是直接使用jdk提供的現成工具,在jdk的安裝的bin目錄下,已經提供了多種命令行監測工具,以便於開發人員和運維人員監測java應用和診斷問題,所以,此類工具是java應用監測的重要手段。也是做爲java開發人員須要掌握的基本技能。linux
通常來講,經常使用的命令行工具包括jps
,jinfo
,jmap
,jstack
,jstat
,這些工具都在JAVA_HOME/bin/
目錄下,概要說明以下:git
jps
查看java進程IDjinfo
查看及調整虛擬機參數jmap
查看堆(heap)使用狀況及生成堆快照jstack
查看線程運行狀態及生成線程快照jstat
顯示進程中的類裝載、內存、垃圾收集等運行數據。經過這些工具,基本上能夠了解java應用的內存變化狀態,線程運行狀態等信息,進而爲應用監測及問題診斷提供依據。下面將結合實例對這些工具的使用進行詳細講解,文中所使用的示例代碼java-monitor-example
已上傳到個人github,地址:https://github.com/mianshenglee
。程序員
jps
jps
說明要監測java應用,第一步就是先知道這個應用是哪一個進程,它的運行參數是什麼。jps
就是能夠查詢進程的工具。熟悉linux的同窗,大概都知道查詢進程使用ps -ef|grep java
這樣的命令,jps也相似,但它不使用名稱查找,而是查找所有當前jdk運行的java進程,並且只查找當前用戶的Java進程,而不是當前系統中的全部進程。github
jps
使用做爲命令行工具,能夠經過-help
參數查看幫助,也可查閱官方文檔:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html
,以下:shell
[root@test bin]# jps -help
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]
Definitions:
<hostid>: <hostname>[:<port>]
參數解釋:
-q:只顯示java進程的pid
-m:輸出傳遞給main方法的參數,在嵌入式jvm上多是null
-l:輸出應用程序main class的完整package名 或者 應用程序的jar文件完整路徑名
-v:輸出傳遞給JVM的參數
複製代碼
示例工程java-monitor-example
在linux機器中運行起來,使用jps
可輸出如下信息:apache
[root@test bin]# jps -q
13680
14214
複製代碼
[root@test bin]# jps -lv
13680 java-monitor-example-0.0.1-SNAPSHOT.jar -Xms128m -Xmx128m -Dserver.port=8083
14289 sun.tools.jps.Jps -Denv.class.path=.:/opt/jdk8/lib:/opt/jdk8/jre/lib -Dapplication.home=/opt/jdk8
複製代碼
輸出的內容中,java-monitor-example-0.0.1-SNAPSHOT.jar
是-l
輸出的完整名稱,-Xms128m -Xmx128m -Dserver.port=8083
是傳給JVM的參數。api
JAVA_HOME="/opt/jdk8"
APP_MAINCLASS=java-monitor-example
#初始化psid變量(全局)
psid=0
#查看進程ID函數
checkpid() {
javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`
if [ -n "$javaps" ]; then
psid=`echo $javaps | awk '{print $1}'`
else
psid=0
fi
}
#調用函數後經過psid進行業務邏輯操做,如根據進程id殺進程
checkpid
echo "(pid=$psid)"
複製代碼
上述腳本,比較適合運維人員對應用的開啓和關閉,自動獲取java進程ID,而後根據ID判斷程序是否運行(start),或者關閉應用(kill -9
)。數組
jinfo
jinfo
說明知道java應用所屬的進程號是第一步,在上一篇文章《java應用監測(2)-java命令的祕密》中,已經知道java的啓動參數有不少,監測java應用前須要瞭解清楚它的啓動參數是什麼。這時就須要用到jinfo
工具。jinfo
能夠輸出JAVA應用的系統參數和JVM參數。jinfo還可以修改一部分運行期間可以調整的虛擬機參數,不少運行參數是不能調整的,若是出現"cannot be changed"異常,說明不能調整。不過官方文檔指出,這個命令在後續的版本中可能再也不使用,當前JDK8仍是能夠用的。
jinfo
使用經過-help
參數查看幫助,也可查閱jinfo
官方文檔說明:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html
,以下:
[root@test bin]# jinfo -help
Usage:
jinfo [option] <pid>
(to connect to running process)
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
複製代碼
使用jps
獲取到應用的進程ID後(示例的PID爲13680),若是直接jps <pid>
則會輸出所有的系統參數和JVM參數,其它參數說明在help
中也說得很清楚了。下面仍是結合示例代碼java-monitor-example
來實踐一下:
[root@test bin]# jinfo -flag InitialHeapSize 13680
-XX:InitialHeapSize=134217728
複製代碼
[root@test bin]# jinfo -flags 13680
Attaching to process ID 13680, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.51-b03
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=134217728 -XX:MaxNewSize=44564480 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=44564480 -XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Xms128m -Xmx128m -Dserver.port=8083
複製代碼
可見,因爲咱們啓動時設置了-Xms
和-Xmx
,它們對應的就是-XX:InitialHeapSize
及-XX:MaxHeapSize
值。另外,參數-Dserver.port
屬於系統參數,使用jinfo -sysprops 13680
就能夠查看系統參數了。
jmap
jmap
說明java應用啓動後,它在JVM中運行,內存是須要重點監測的地方,jmap
就是這樣的一個工具,它能夠獲取運行中的jvm的堆的快照,包括總體狀況,堆佔用狀況的直方圖,dump出快照文件以便於離線分析等。官方文檔指出,這個命令在後續的版本中可能再也不使用,當前JDK8仍是能夠用的。
jmap
使用經過-help
參數查看幫助,也可查閱jmap
官方文檔說明:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html
,幫助說明以下:
[root@test bin]# jmap -help
Usage:
jmap [option] <pid>
(to connect to running process)
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
參數經常使用的是-heap
,-histo
和-dump
,結合示例java-monitor-example
,說明以下:
[root@test bin]# jmap -heap 13680
Attaching to process ID 13680, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.51-b03
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 134217728 (128.0MB)
NewSize = 44564480 (42.5MB)
MaxNewSize = 44564480 (42.5MB)
OldSize = 89653248 (85.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 31981568 (30.5MB)
used = 5306632 (5.060798645019531MB)
free = 26674936 (25.43920135498047MB)
16.59278244268699% used
From Space:
capacity = 6291456 (6.0MB)
used = 1081440 (1.031341552734375MB)
free = 5210016 (4.968658447265625MB)
17.18902587890625% used
To Space:
capacity = 6291456 (6.0MB)
used = 0 (0.0MB)
free = 6291456 (6.0MB)
0.0% used
PS Old Generation
capacity = 89653248 (85.5MB)
used = 16615680 (15.845947265625MB)
free = 73037568 (69.654052734375MB)
18.533271655701753% used
18006 interned Strings occupying 2328928 bytes.
複製代碼
從以上信息,能夠看出JVM中堆內存當前的使用狀況,包括年輕代(Eden
區,From
區,To
區)和年老代。
[root@test bin]# jmap -histo:live 13680|more
num #instances #bytes class name
----------------------------------------------
1: 36536 6462912 [C
2: 35557 853368 java.lang.String
3: 7456 826968 java.lang.Class
4: 20105 643360 java.util.concurrent.ConcurrentHashMap$Node
5: 1449 469024 [B
6: 6951 399280 [Ljava.lang.Object;
7: 9311 297952 java.util.HashMap$Node
8: 3122 274736 java.lang.reflect.Method
9: 2884 269112 [I
10: 6448 257920 java.util.LinkedHashMap$Entry
11: 2994 255160 [Ljava.util.HashMap$Node;
12: 15249 243984 java.lang.Object
.....
.....
複製代碼
如上所示,使用-histo
輸出包括序號,實例數,佔用字節數和類名稱。具體說明以下:
- instances列:表示當前類有多少個實例。
- bytes列:說明當前類的實例總共佔用了多少個字節
- class name列:表示的就是當前類的名稱,class name 解讀:
- B表明byte
- C表明char
- D表明double
- F表明float
- I表明int
- J表明long
- Z表明boolean
- [表明數組,如[I至關於int[]
- 對象用[L+類名錶示
[root@test bin]# jmap -dump:file=./heap.hprof 13680
複製代碼
如上所示,會把堆狀況寫入到當前目錄的heap.hprof
文件中,至於如何分析此文件,可使用jhat
,但通常實際開發中,不多使用jhat來直接對內存dump文件進行分析,所以再也不對它進行講述。更多的是使用工具MAT
,以可視化的方式來查看,後續文章將會對MAT
工具的使用進行詳細講解。
jstack
jstack
說明此命令打印指定Java應用的線程堆棧,對於每一個Java幀,將打印完整的類名,方法名,字節代碼索引(BCI)和行號,能夠用於檢測死鎖,線程停頓,進程耗用cpu太高報警問題等排查。
jstack
使用使用-help
參數查看幫助,也可查閱jstack
官方文檔說明:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstack.html
,幫助說明以下:
[root@test bin]# jstack -help
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
Options:
-F 強制dump線程堆棧信息. 用於進程hung住, jstack <pid>命令沒有響應的狀況
-m 同時打印java和本地(native)線程棧信息,m是mixed mode的簡寫
-l 打印鎖的額外信息
複製代碼
結合示例java-monitor-example
,能夠打印線程信息(通常都會把打印的內容寫入到文件而後再分析),以下:
[root@test bin]# jstack 13680
2019-08-16 23:18:18
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.51-b03 mixed mode):
"http-nio-8083-Acceptor-0" #39 daemon prio=5 os_prio=0 tid=0x00007f7520698800 nid=0x359a runnable [0x00007f7508bb7000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x00000000f8c85380> (a java.lang.Object)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:448)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:70)
at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95)
at java.lang.Thread.run(Thread.java:745)
複製代碼
java線程棧使用jstack
dump出來後,能夠看到線程的狀態,線程狀態一共分6種,能夠參考官方文檔:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr034.html
,下面是它的狀態說明:
線程已經new出來建立了,可是尚未啓動(not yet started),jstack
不會打印這個狀態的線程信息。
正在Java虛擬機下跑任務的線程的狀態,但其實它是隻是表示線程是可運行的(ready)。對於單核的CPU,多個線程在同一時刻,只能運行一個線程,其它的則須要等CPU的調度。
線程處於阻塞狀態,正在等待一個鎖,多個線程共用一個鎖,當某線程正在使用這個鎖進入某個synchronized同步方法塊或者方法,而此線程須要進入這個同步代碼塊,也須要這個鎖,則致使本線程處於阻塞狀態。
等待狀態,處於等待狀態的線程是因爲執行了3個方法中的任意方法。 1. Object.wait
方法,而且沒有使用timeout參數; 2. Thread.join
方法,沒有使用timeout參數 3. LockSupport.park
方法。 處於waiting狀態的線程會等待另一個線程處理特殊的行爲。一個線程處於等待狀態(wait,一般是在等待其餘線程完成某個操做(notify或者notifyAll)。注意,Object.wait()
方法只可以在同步代碼塊中調用。調用了wait()
方法後,會釋放鎖。
線程等待指定的時間,對於如下方法的調用,可能會致使線程處於這個狀態:1. Thread.sleep方法 2. Object.wait
方法,帶有時間 3. Thread.join
方法,帶有時間 4. LockSupport.parkNanos
方法,帶有時間 5. LockSupport.parkUntil
方法,帶有時間。注意,Thread.sleep
方法調用後,它不會釋放鎖,仍然佔用系統資源。
線程停止的狀態,這個線程已經完整地執行了它的任務。
從下面這張圖能夠看出線程狀態的變化狀況:
jstack
後線程棧內容從前面使用jstack
dump出來信息,咱們須要知道如下幾個信息:
"http-nio-8083-Acceptor-0" #39
:是線程的名字,所以,通常咱們建立線程時須要設置本身能夠辯識的名字。daemon
表示線程是不是守護線程prio
表示咱們爲線程設置的優先級os_prio
表示的對應的操做系統線程的優先級,因爲並非全部的操做系統都支持線程優先級,因此可能會出現都置爲0的狀況tid
線程的idnid
線程對應的操做系統本地線程id,每個java線程都有一個對應的操做系統線程,它是16進制的,所以通常在操做系統中獲取到線程ID後,須要轉爲16進制,來對應上。java.lang.Thread.State: RUNNABLE
運行狀態,上面已經介紹了線程的狀態,如果WAITING狀態,則括號中的內容說明了致使等待的緣由,如parking說明是由於調用了LockSupport.park方法致使等待。一般的堆棧信息中,都會有lock標記,如- locked <0x00000000f8c85380> (a java.lang.Object)
表示正在佔用這個鎖。wait
狀態的線程jstat
jstat
說明jstat
即JVM Statistics Monitoring Tool
,即JVM統計監測工具,包括監測類裝載、內存、垃圾收集、JIT編譯等運行數據,在沒有圖形的服務器上,它是運行期定位虛擬機性能問題的首選工具。
jstat
使用使用-help
參數查看幫助,也可查閱jstat
官方文檔說明:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
,幫助說明以下:
[root@test bin]# jstat -help
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.
複製代碼
以上所示,vmid
、interval
、count
分別是進程號,打印間隔時間(s或ms),打印次數,其中option
參數主要是如下(也可使用命令jstat -option
查看):
-class
統計class loader行爲信息 ,如總共加載了多少個類-compile
統計HotSpot Just-in-Time編譯器的行爲-gc
統計jdk gc時heap信息-gccapacity
統計不一樣的generations相應的heap容量狀況-gccause
統計gc的狀況,(同-gcutil)和引發gc的事件-gcnew
統計gc時,新生代的狀況-gcnewcapacity
統計gc時,新生代heap容量-gcold
統計gc時,老年區的狀況-gcoldcapacity
統計gc時,老年區heap容量-gcpermcapacity
統計gc時,permanent區heap容量-gcutil
統計gc時,heap狀況-printcompilation
hotspot編譯方法統計通常咱們使用-class
,-gc
,-gccause
和-gcutil
比較多,主要用於來分析類和堆使用狀況及gc狀況。
以上述的示例工程java-monitor-example
爲例,裏面包含了一個函數來測試內存溢出(使用一個數組,循環建立對象,直到內存溢出)。使用jstat -gc 13680 1000
即每秒監測一次,調用/monitor/user/oom
接口後,即看到堆和GC變化狀況。爲方便查看,我把輸出放到sublime中顯示,以下所示:
日誌輸出OOM報錯:
以上輸出的內容,每列的說明以下:
S0C
當前年輕代中第一個survivor(s0)的總容量 (KB).S1C
當前年輕代中第一個survivor(s1)的總容量 (KB).S0U
s0已使用的容量 (KB).S1U
s1已使用的容量 (KB).EC
當前年輕代中eden區總容量 (KB).EU
eden區已經使用的容量 (KB).OC
年老代的容量總容量 (KB).OU
年老代已使用容量(KB).MC
當前 Metaspace總容量(KB).MU
當前 Metaspace已使用容量 (KB).CCSC
Compressed class容量大小CCSU
Compressed class已使用容量YGC
從應用啓動時到如今,年輕代young generation 發生GC Events的總次數.YGCT
從應用啓動時到如今, 年輕代Young generation 垃圾回收的總耗時.FGC
從應用啓動時到如今, full GC事件總次數.FGCT
從應用啓動時到如今, Full sc總耗時.GCT 從應用啓動時到如今, 垃圾回收總時間. - GCT
GCT=YGCT+FGCT從以上輸出第6行能夠看出,EC
和EU
,OC
和OU
表示年輕代、年老代的內存都已經用完(與容量數值相等),發生OOM。這時,則須要採起措施,增大內存(-Xmx參數)或者找到致使OOM的代碼進行修改。
針對java應用的監測,本文對jdk提供自身提供的命令行工具進行了說明和使用的介紹,完整的描述了查看java應用進程,查看啓動參數,查看內存狀況,查看線程狀況,查看內存統計狀況等,主要是jps
,jinfo
,jmap
,jstack
,jstat
5個工具,並結合實例,但願學習java開發人員都能掌握這些技術,在監測java應用時,能夠從容面對如OOM,CPU高,線程停頓等問題。
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/
java-monitor-example
:https://github.com/mianshenglee/my-example/tree/master/java-monitor-example
https://www.hollischuang.com/archives/1561
http://www.tianshouzhi.com/api/tutorials/jvm/346