JVM排查指令
本文将介紹JDK自帶的JVM排查工具。其提供的排查工具有:
(1)jps:JVM Process Status Tool,顯示系統内所有的JVM程序;
(2)jstat:JVM Statistics Monitoring Tool,可以收集JVM相關的運作資料;
(3)jinfo:Configuration Info for Java,顯示JVM配置資訊;
(4)jmap:Memory Map for Java,用于生成JVM的記憶體快照;
(5)jhat:JVM Heap Dump Browser,用于分析heapdump檔案,它可以建立一個http/html服務,使用者可以在浏覽器上檢視分析結果;
(6)jstack:Stack Trace for Java,顯示JVM的線程快照。
(7)jconsole:一個java GUI監視工具,可以以圖表化的形式顯示各種資料。并可通過遠端連接配接監視遠端的伺服器VM。
一、jps
列出正在運作的虛拟機程序,并顯示虛拟機執行主類名稱以及這些程序的本地虛拟機唯一id(Local Virtual Machine Identifier,簡稱LVMID)。相比其他指令來說,它的功能其實比較單一,但是它的使用頻率确實非常高的,因為其他的JDK指令大多都需要輸入虛拟機程序id。
注:看到這裡小夥伴們可能就會比較困惑了,因為在大多數使用JDK指令時,我們都用的是pid(作業系統程序id)啊,那pid和LVMID到底有什麼差別呢?其實,很負責任的告訴你,對于本地虛拟機程序來說,它倆無差别,用ps指令也可以查詢到,但是如果同時啟動多個虛拟機程序無法根據程序名稱定位時,jps指令就派上用場了,可以輸出主類名稱,通過主類名稱來區分。
jps指令格式:
jps主要提供以下選項:
-q
隻輸出LVMID,省略主類名稱;
-m
輸出虛拟機程序啟動時傳給主類函數的參數;
-l
輸出主類的完成package名稱或者jar包完整路徑名;
-v
輸出虛拟機啟動時的JVM參數
二、jstat
用于監控虛拟機運作狀态資訊。它可以顯示本地或者遠端虛拟機程序的記憶體、垃圾收集、JIT編譯等運作資料。
jstat指令格式:
jstat指令稍許有些複雜,它主要有以下參數:
option:選項,jstat主要提供以下選項:
-class
監視類的裝載/解除安裝數量、總空間以及類裝載所耗時間;
-gc
監視java heap情況,包括eden區和兩個survivor區、old區、永久區等的容量,已用空間和GC時間等資訊;
-gccapacity
監視内容與-gc基本是一緻的,-gccapacity的輸出包括heap各個區域使用到的最大最小空間;
gcutil
監視内容同樣與-gc基本一緻,-gcutil的輸出主要是heap各個區域使用空間占總空間百分比;
gccause
與-gcutil功能一緻,但是會額外輸出導緻上一次gc的原因與目前gc的原因;
LGCC:最近垃圾回收的原因
GCC:目前垃圾回收的原因
gcnew
監視young區gc情況;
gcnewcapacity
監視内容與-gcnew基本相同,-gcnewcapacity的輸出包括使用到的最大最小空間;
-gcold
監視old區gc情況;
-gcoldcapacity
監視内容與-gcold基本相同,-gcoldcapacity的輸出包括使用到的最大最小空間;
-gcpermcapacity
輸出永久代使用到的最大最小空間;
注:JDK 8廢除了永久代,引入了Metaspace,這個指令在JDK 8的環境下就不能使用了,那要看中繼資料空間相關情況,使用-gcmetacapacity即可
-compiler
輸出JIT編譯器編譯過的方法以及耗時等資訊;
-printcompilation
輸出以及被JIT編譯的方法
vmid:虛拟機程序id,這時候小夥伴們肯定又要開始疑惑了,這個vmid與lvmid又有什麼差別?其實對于本地虛拟機程序,它倆沒任何差別,但是如果是遠端虛拟機程序,它倆就有差別了,遠端虛拟機程序vmid格式應該是這樣:
[protocol:][//] lvmid [@hostname[:port]/servername];
interval:查詢時間間隔;
count:查詢次數。
注:如果參數interval和count省略則代表隻查詢一次;如果count省略的話,就會一直查詢。來個簡單的例子:jstat -gcnewcapacity 41503 1000,表示輸出程序41503的young區使用及gc情況,每1000ms輸出一次。
接下來就部分option給出執行個體,同時分析下輸出。
jstat使用
jstat -class <vmid>
jstat -class輸出
Loaded:裝載的類的數量;
Bytes:裝載類所占用的位元組數;
Unloaded:解除安裝類數量;
Bytes:解除安裝類所占用的位元組數;
Time:裝載和解除安裝類所花時間。
jstat -compiler <vmid>
jstat -compiler輸出
Compiled:編譯任務執行數量;
Failed:編譯任務執行失敗數量;
Invalid:編譯任務執行失效數量;
Time:編譯任務消耗時間;
FailedType:最後一個編譯失敗任務的類型;
FailedMethod:最後一個編譯失敗任務所在的類及方法。
jstat -gc <vmid>
jstat -gc輸出
S0C:young區中的第一個survivor區的大小;
S1C:young區中的第二個survivor區的大小;
S0U:young區中的第一個survivor區目前已使用空間;
S1U:young區中的第二個survivor區目前已使用空間;
EC:young區中的eden區的大小;
EU:young區中的eden區目前已使用空間;
OC:old區的大小;
OU:old區目前已使用空間;
MC:中繼資料區大小;
MU:中繼資料區使用大小;
CCSC:壓縮類空間大小;
CCSU:壓縮類空間使用大小;
YGC:young gc次數;
YGCT:young gc消耗時間;
FGC:full gc次數;
FGCT:full gc消耗時間;
GCT:gc消耗時間。
jstat -gcutil <vmid>
jstat -gcutil輸出
S0:young區中的第一個survivor區的使用比例;
S1:young區中的第二個survivor區的使用比例;
E:young區中的eden區的使用比例;
O:old區使用比例;
M:中繼資料區使用比例;
CCS:壓縮類空間使用比例;
YGC:young gc次數;
YGCT:young gc消耗時間;
FGC:full gc次數;
FGCT:full gc消耗時間;
GCT:gc消耗時間。
jstat -gccapacity <vmid>
jstat -gccapacity輸出
NGCMN:young區最小容量;
NGCMX:young區最大容量;
NGC:目前young區容量;
S0C:young區中的第一個survivor區的大小;
S1C:young區中的第二個survivor區的大小;
EC:young區中的eden區的大小;
OGCMN:old區最小容量;
OGCMX:old區最大容量;
OGC:目前old區大小;
OC:目前old區的大小;
MCMN:中繼資料區最小容量;
MCMX:中繼資料區最大容量;
MC:目前中繼資料區大小;
CCSMN:壓縮類空間最小容量;
CCSMX:壓縮類空間最大容量;
CCSC:目前壓縮類空間大小;
YGC:young gc次數;
FGC:old gc次數。
jstat -gcnew <vmid>
jstat -gcnew輸出
S0C:young區中的第一個survivor區的大小;
S1C:young區中的第二個survivor區的大小;
S0U:young區中的第一個survivor區目前已使用空間;
S1U:young區中的第二個survivor區目前已使用空間;
TT:對象在young區存活的次數;
MTT:對象在young區存活的最大次數;
DSS:期望survivor區大小;
EC:young區中的eden區的大小;
EU:young區中的eden區目前已使用空間;
YGC:young gc次數;
YGCT:young gc消耗時間。
jstat -gcold <vmid>
jstat -gcold輸出
MC:中繼資料空間大小;
MU:中繼資料空間使用大小;
CCSC:壓縮類空間大小;
CCSU:壓縮類空間使用大小;
OC:old區大小;
OU:old區使用大小;
YGC:young gc次數;
FGC:full gc次數;
FGCT:full gc消耗時間;
GCT:gc消耗時間。
jstat -gcmetacapacity <vmid>
jstat -gcmetacapacity輸出
MCMN:中繼資料區最小容量;
MCMX:中繼資料區最大容量;
MC:中繼資料區大小;
CCSMN:壓縮類空間最小容量;
CCSMX:壓縮類空間最大容量;
CCSC:壓縮類空間大小;
YGC:young gc次數;
FGC:full gc次數;
FGCT:full gc消耗時間;
GCT:gc消耗時間。
jstat -printcompilation <vmid>
jstat -printcompilation輸出
Compiled:編譯方法數量;
Size:編譯方法的位元組碼數量;
Type:編譯方法的編譯類型;
Method:方法名稱。
針對young區和old區相關capacity指令在這裡就不做詳細分析了,有興趣的小夥伴自行敲一敲指令運作下,至于輸出的表格列含義在前幾個指令詳細介紹中基本上都包括在内了。
注:在使用jstat指令輸出的容量的機關是位元組。
三、jinfo
用于實時檢視和調整虛拟機參數。
jinfo指令格式
jinfo指令主要有以下參數:
option:選項,jinfo主要提供以下選項:
-flag <name>
輸出指定JVM參數值;
-flag [+|-]<name>
啟用或禁用指定JVM參數;
-flag <name>=<value>
設定指定JVM參數值;
-flags
輸出所有JVM參數
-sysprops
輸出Java系統屬性;
<no option>
不指定選項則輸出所有的虛拟機參數和Java系統屬性
pid:需要檢視或者調整虛拟機參數的程序id
四、jmap
用于生成堆記憶體快照(heapdump或者dump檔案)。當然,如果不想使用jmap指令,也可以使用JVM參數來生成:
-XX:+HeapDumpOnOutOfMemoryError,如果虛拟機在出現OutOfMemory異常後生成dump檔案;
-XX:+HeapDumpOnCtrlBreak,使用ctrl + break鍵讓虛拟機生成dump檔案。
當然,還有一種更暴力的方式就是在linux系統下,kill -3也可以讓虛拟機生成dump檔案。
jmap指令格式
相比jstat指令,jmap指令明顯就簡單的多了,就兩個參數:
option:選項,jmap主要提供以下選項:
-dump
生成Java堆記憶體快照,使用格式為:-dump:[live, ]format=b,file=<filename>,使用hprof二進制形式,輸出jvm的heap内容到檔案<filename>,live子參數是可選的,如果指定live選項,就隻dump出存活的對象;
finalizerinfo
顯示在F-Queue中等待Finalizer線程執行finalize方法的對象;
-heap
顯示heap詳細資訊,比如使用哪種回收器、參數配置、分代狀态等;
histo
顯示每個class的執行個體數目,記憶體占用,類全名資訊。VM的内部類名字開頭會加上字首“*”.,如果帶上live子參數,則隻統計活的對象數量;
-permstat
以ClassLoader為統計口徑顯示永久代記憶體狀态,需要注意的是,JDK 8将該option替換成了-clstats;
-F
強制生成dump檔案,當虛拟機程序對-dump選項沒有響應時可以使用。
pid:需要生成dump檔案的程序id
jmap -histo輸出
從輸出的結果可以清晰的看出每一個class的執行個體數目以及記憶體占用情況。
五、jstack
使用者生成虛拟機目前時刻的線程快照(threaddump/javacore檔案)。線程快照就是目前虛拟機内每一條線程正在執行的方法堆棧集合,生成線程快照的目的也就是為了定位線程出現長時間卡頓的原因。
jstack指令格式
跟jmap指令一樣,jstack指令也隻有兩個參數:
option:選項,jstack主要提供以下選項:
-F
當線程出現長時間卡頓的時候,強制輸出線程堆棧;
-l
除堆棧外,顯示關于鎖的附加資訊;
-m
如果調用JNI方法,可以顯示C/C++的堆棧。
pid:需要生成threaddump的程序id。
jstack -l輸出
從輸出結果可以清晰的看到線程堆棧以及鎖相關資訊。
六、jhat
與jmap指令搭配使用,分析jmap生成的dump檔案。jhat内置一個微型的HTTP/HTML伺服器,生成dump檔案的分析結果後,可以在浏覽器中進行檢視。其實這個指令在實際生産中使用比較少,為什麼不用的原因我總結下來大概有兩點:第一呢,線上生産環境一般不允許線上上機器直接分析dump檔案;第二就是jhat的分析功能相比其他工具簡直是太簡陋了。
它的使用也很簡單啊,jhat <dump檔案名稱>,等分析完就打開浏覽器通路相應的位址+端口就可以愉快的開始分析dump檔案了。
七、jconsole
jconsole是一個用java寫的GUI程式,用來監控VM,并可監控遠端的VM,非常易用,而且功能非常強。使用方法:指令行裡打 jconsole,選則程序就可以了。
Console中關于記憶體分區的說明。
Eden Space (heap): 記憶體最初從這個線程池配置設定給大部分對象。
Survivor Space (heap):用于儲存在eden space記憶體池中經過垃圾回收後沒有被回收的對象。
Tenured Generation (heap):用于保持已經在 survivor space記憶體池中存在了一段時間的對象。
Permanent Generation (non-heap): 儲存虛拟機自己的靜态(refective)資料,例如類(class)和方法(method)對象。Java虛拟機共享這些類資料。這個區域被分割為隻讀的和隻寫的, 叫做“代碼緩存區”(code cache)