注:最近一直想出一篇介紹JVM底層函數(shù)調(diào)用的博客,奈何越寫越多,現(xiàn)在還沒寫完穿剖,先來個簡單的安慰下我受傷的心靈
滴滴...報警短信又來了~~~打開一看冬竟,xxx系統(tǒng)內(nèi)存使用率達(dá)90%欧穴,請盡快處理
。一臉懵逼泵殴,昨天還60%涮帘,今天怎么就90%了,內(nèi)存泄漏了么笑诅?
相信這個場景絕大多數(shù)猿都不陌生调缨,出現(xiàn)這個問題了我們該怎么辦呢?Java的內(nèi)存分配和垃圾收集都對開發(fā)者不透明吆你,我們要怎么排查這類型的問題呢弦叶?接下來,就讓我早处,來給你們介紹幾款排查JVM問題的小工具湾蔓。
JDK提供的命令工具
作為一個Java程序猿,大家肯定都知道jdk的bin目錄下有java
砌梆,javac
這倆命令默责,但是bin下其實還有好多其他的命令贬循,我相信并不是所有的程序猿都非常了解這些命令。這次我要介紹排查JVM問題的小工具呢也就在bin目錄下桃序。先來看下bin目錄下的命令集們:
這些可愛的命令集們是java開發(fā)者們送給JDK使用者們的小禮物杖虾,它們可以幫助我們快速在遇到JVM問題可以快速排查并定位bug。
注:bin目錄下的命令集們體積都很小媒熊,體積小并不是java開發(fā)者們在炫耀技術(shù)奇适,而是這些命令其實大多都是包裝
jdk/lib/tools.jar
庫,至于為什么選擇用java來實現(xiàn)這些命令也是為了便于在應(yīng)用程序中直接實現(xiàn)JVM的監(jiān)控和分析芦鳍。
排查JVM問題命令工具
JDK提供以下命令排查JVM問題:
jps:JVM Process Status Tool嚷往,顯示系統(tǒng)內(nèi)所有的JVM進(jìn)程;
jstat:JVM Statistics Monitoring Tool柠衅,可以收集JVM相關(guān)的運行數(shù)據(jù)皮仁;
jinfo:Configuration Info for Java,顯示JVM配置信息菲宴;
jmap:Memory Map for Java贷祈,用于生成JVM的內(nèi)存快照;
jhat:JVM Heap Dump Browser喝峦,用于分析heapdump文件势誊,它可以建立一個http/html服務(wù),使用者可以在瀏覽器上查看分析結(jié)果谣蠢;
jstack:Stack Trace for Java粟耻,顯示JVM的線程快照。
接下來就針對這幾個命令的使用做相關(guān)分析漩怎。
jps
列出正在運行的虛擬機進(jìn)程勋颖,并顯示虛擬機執(zhí)行主類名稱以及這些進(jìn)程的本地虛擬機唯一id(Local Virtual Machine Identifier,簡稱LVMID)勋锤。相比其他命令來說饭玲,它的功能其實比較單一,但是它的使用頻率確實非常高的叁执,因為其他的JDK命令大多都需要輸入虛擬機進(jìn)程id茄厘。
注:看到這里小伙伴們可能就會比較困惑了,因為在大多數(shù)使用JDK命令時谈宛,我們都用的是pid(操作系統(tǒng)進(jìn)程id)啊次哈,那pid和LVMID到底有什么區(qū)別呢?其實吆录,很負(fù)責(zé)任的告訴你窑滞,對于本地虛擬機進(jìn)程來說,它倆無差別,用ps命令也可以查詢到哀卫,但是如果同時啟動多個虛擬機進(jìn)程無法根據(jù)進(jìn)程名稱定位時巨坊,jps命令就派上用場了,可以輸出主類名稱此改,通過主類名稱來區(qū)分趾撵。
jps命令格式:
jps主要提供以下選項:
-q
只輸出LVMID,省略主類名稱共啃;-m
輸出虛擬機進(jìn)程啟動時傳給主類函數(shù)的參數(shù)占调;-l
輸出主類的完成package名稱或者jar包完整路徑名;-v
輸出虛擬機啟動時的JVM參數(shù)
接下來我們來看看它的實際使用:
-
測試代碼:測試代碼
-
使用結(jié)果:jps使用結(jié)果
jstat
用于監(jiān)控虛擬機運行狀態(tài)信息移剪。它可以顯示本地或者遠(yuǎn)程虛擬機進(jìn)程的內(nèi)存究珊、垃圾收集、JIT編譯等運行數(shù)據(jù)挂滓。
jstat命令格式:
jstat命令稍許有些復(fù)雜苦银,它主要有以下參數(shù):
-
option:選項,jstat主要提供以下選項:
-class
監(jiān)視類的裝載/卸載數(shù)量赶站、總空間以及類裝載所耗時間;-gc
監(jiān)視java heap情況纺念,包括eden區(qū)和兩個survivor區(qū)贝椿、old區(qū)、永久區(qū)等的容量陷谱,已用空間和GC時間等信息烙博;-gccapacity
監(jiān)視內(nèi)容與-gc
基本是一致的,-gccapacity
的輸出包括heap各個區(qū)域使用到的最大最小空間烟逊;gcutil
監(jiān)視內(nèi)容同樣與-gc
基本一致渣窜,-gcutil
的輸出主要是heap各個區(qū)域使用空間占總空間百分比;gccause
與-gcutil
功能一致宪躯,但是會額外輸出導(dǎo)致上一次gc的原因乔宿;gcnew
監(jiān)視young區(qū)gc情況;gcnewcapacity
監(jiān)視內(nèi)容與-gcnew
基本相同访雪,-gcnewcapacity
的輸出包括使用到的最大最小空間详瑞;-gcold
監(jiān)視old區(qū)gc情況;-gcoldcapacity
監(jiān)視內(nèi)容與-gcold
基本相同臣缀,-gcoldcapacity
的輸出包括使用到的最大最小空間坝橡;-gcpermcapacity
輸出永久代使用到的最大最小空間;
注:JDK 8廢除了永久代精置,引入了Metaspace计寇,這個命令在JDK 8的環(huán)境下就不能使用了,那要看元數(shù)據(jù)空間相關(guān)情況,使用
-gcmetacapacity
即可-
-compiler
輸出JIT編譯器編譯過的方法以及耗時等信息番宁; -
-printcompilation
輸出以及被JIT編譯的方法
vmid:虛擬機進(jìn)程id元莫,這時候小伙伴們肯定又要開始疑惑了,這個vmid與lvmid又有什么區(qū)別贝淤?其實對于本地虛擬機進(jìn)程柒竞,它倆沒任何區(qū)別,但是如果是遠(yuǎn)程虛擬機進(jìn)程播聪,它倆就有區(qū)別了朽基,遠(yuǎn)程虛擬機進(jìn)程vmid格式應(yīng)該是這樣:
[protocol:][//] lvmid [@hostname[:port]/servername]
;interval:查詢時間間隔离陶;
count:查詢次數(shù)稼虎。
注:如果參數(shù)interval和count省略則代表只查詢一次;如果count省略的話招刨,就會一直查詢霎俩。來個簡單的例子:
jstat -gcnewcapacity 41503 1000
,表示輸出進(jìn)程41503的young區(qū)使用及gc情況沉眶,每1000ms輸出一次打却。
接下來就部分option給出實例,同時分析下輸出谎倔。
jstat使用
-
jstat -class <vmid>
jstat -class輸出Loaded:裝載的類的數(shù)量柳击;
Bytes:裝載類所占用的字節(jié)數(shù);
Unloaded:卸載類數(shù)量片习;
Bytes:卸載類所占用的字節(jié)數(shù)捌肴;
Time:裝載和卸載類所花時間。
-
jstat -compiler <vmid>
jstat -compiler輸出Compiled:編譯任務(wù)執(zhí)行數(shù)量藕咏;
Failed:編譯任務(wù)執(zhí)行失敗數(shù)量状知;
Invalid:編譯任務(wù)執(zhí)行失效數(shù)量;
Time:編譯任務(wù)消耗時間孽查;
FailedType:最后一個編譯失敗任務(wù)的類型饥悴;
FailedMethod:最后一個編譯失敗任務(wù)所在的類及方法。
-
jstat -gc <vmid>
jstat -gc輸出S0C:young區(qū)中的第一個survivor區(qū)的大胸阅搿铺坞;
S1C:young區(qū)中的第二個survivor區(qū)的大小洲胖;
S0U:young區(qū)中的第一個survivor區(qū)目前已使用空間济榨;
S1U:young區(qū)中的第二個survivor區(qū)目前已使用空間;
EC:young區(qū)中的eden區(qū)的大新逃场擒滑;
EU:young區(qū)中的eden區(qū)目前已使用空間腐晾;
OC:old區(qū)的大小丐一;
OU:old區(qū)目前已使用空間藻糖;
MC:元數(shù)據(jù)區(qū)大小库车;
MU:元數(shù)據(jù)區(qū)使用大芯奁狻;
CCSC:壓縮類空間大心堋洋满;
CCSU:壓縮類空間使用大小珍坊;
YGC:young gc次數(shù)牺勾;
YGCT:young gc消耗時間;
FGC:full gc次數(shù)阵漏;
FGCT:full gc消耗時間驻民;
GCT:gc消耗時間。
-
jstat -gcutil <vmid>
jstat -gcutil輸出S0:young區(qū)中的第一個survivor區(qū)的使用比例履怯;
S1:young區(qū)中的第二個survivor區(qū)的使用比例回还;
E:young區(qū)中的eden區(qū)的使用比例;
O:old區(qū)使用比例叹洲;
M:元數(shù)據(jù)區(qū)使用比例懦趋;
CCS:壓縮類空間使用比例;
YGC:young gc次數(shù)疹味;
YGCT:young gc消耗時間;
FGC:full gc次數(shù)帜篇;
FGCT:full gc消耗時間糙捺;
GCT:gc消耗時間。
-
jstat -gccapacity <vmid>
jstat -gccapacity輸出NGCMN:young區(qū)最小容量笙隙;
NGCMX:young區(qū)最大容量洪灯;
NGC:當(dāng)前young區(qū)容量;
S0C:young區(qū)中的第一個survivor區(qū)的大芯固怠签钩;
S1C:young區(qū)中的第二個survivor區(qū)的大小坏快;
EC:young區(qū)中的eden區(qū)的大星﹂荨;
OGCMN:old區(qū)最小容量莽鸿;
OGCMX:old區(qū)最大容量昧旨;
OGC:當(dāng)前old區(qū)大惺案;
OC:當(dāng)前old區(qū)的大型梦帧蒋得;
MCMN:元數(shù)據(jù)區(qū)最小容量;
MCMX:元數(shù)據(jù)區(qū)最大容量乒疏;
MC:當(dāng)前元數(shù)據(jù)區(qū)大卸钛谩;
CCSMN:壓縮類空間最小容量怕吴;
CCSMX:壓縮類空間最大容量窍侧;
CCSC:當(dāng)前壓縮類空間大小械哟;
YGC:young gc次數(shù)疏之;
FGC:old gc次數(shù)。
-
jstat -gcnew <vmid>
jstat -gcnew輸出S0C:young區(qū)中的第一個survivor區(qū)的大邢九亍锋爪;
S1C:young區(qū)中的第二個survivor區(qū)的大小爸业;
S0U:young區(qū)中的第一個survivor區(qū)目前已使用空間其骄;
S1U:young區(qū)中的第二個survivor區(qū)目前已使用空間;
TT:對象在young區(qū)存活的次數(shù)扯旷;
MTT:對象在young區(qū)存活的最大次數(shù)拯爽;
DSS:期望survivor區(qū)大小钧忽;
EC:young區(qū)中的eden區(qū)的大刑号凇;
EU:young區(qū)中的eden區(qū)目前已使用空間耸黑;
YGC:young gc次數(shù)桃煎;
YGCT:young gc消耗時間。
-
jstat -gcold <vmid>
jstat -gcold輸出MC:元數(shù)據(jù)空間大写罂为迈;
MU:元數(shù)據(jù)空間使用大小缺菌;
CCSC:壓縮類空間大泻;
CCSU:壓縮類空間使用大邪橛簟耿战;
OC:old區(qū)大小蛾绎;
OU:old區(qū)使用大欣セ鸦列;
YGC:young gc次數(shù);
FGC:full gc次數(shù)鹏倘;
FGCT:full gc消耗時間薯嗤;
GCT:gc消耗時間。
-
jstat -gcmetacapacity <vmid>
jstat -gcmetacapacity輸出MCMN:元數(shù)據(jù)區(qū)最小容量纤泵;
MCMX:元數(shù)據(jù)區(qū)最大容量骆姐;
MC:元數(shù)據(jù)區(qū)大小捏题;
CCSMN:壓縮類空間最小容量玻褪;
CCSMX:壓縮類空間最大容量;
CCSC:壓縮類空間大泄带射;
YGC:young gc次數(shù);
FGC:full gc次數(shù)循狰;
FGCT:full gc消耗時間窟社;
GCT:gc消耗時間。
-
jstat -printcompilation <vmid>
jstat -printcompilation輸出Compiled:編譯方法數(shù)量绪钥;
Size:編譯方法的字節(jié)碼數(shù)量灿里;
Type:編譯方法的編譯類型;
Method:方法名稱程腹。
針對young區(qū)和old區(qū)相關(guān)capacity命令在這里就不做詳細(xì)分析了匣吊,有興趣的小伙伴自行敲一敲命令運行下,至于輸出的表格列含義在前幾個命令詳細(xì)介紹中基本上都包括在內(nèi)了寸潦。
注:在使用jstat命令輸出的容量的單位是字節(jié)色鸳。
jinfo
用于實時查看和調(diào)整虛擬機參數(shù)。
jinfo命令格式
jinfo命令主要有以下參數(shù):
-
option:選項见转,jinfo主要提供以下選項:
-flag <name>
輸出指定JVM參數(shù)值缕碎;-flag [+|-]<name>
啟用或禁用指定JVM參數(shù);-flag <name>=<value>
設(shè)置指定JVM參數(shù)值池户;-flags
輸出所有JVM參數(shù)-sysprops
輸出Java系統(tǒng)屬性;<no option>
不指定選項則輸出所有的虛擬機參數(shù)和Java系統(tǒng)屬性
pid:需要查看或者調(diào)整虛擬機參數(shù)的進(jìn)程id
接下來我們來看看它的實際使用:
jmap
用于生成堆內(nèi)存快照(heapdump或者dump文件)凡怎。當(dāng)然校焦,如果不想使用jmap命令,也可以使用JVM參數(shù)來生成:
-XX:+HeapDumpOnOutOfMemoryError
统倒,如果虛擬機在出現(xiàn)OutOfMemory異常后生成dump文件寨典;-XX:+HeapDumpOnCtrlBreak
,使用ctrl + break鍵讓虛擬機生成dump文件房匆。
當(dāng)然耸成,還有一種更暴力的方式就是在linux系統(tǒng)下报亩,kill -3也可以讓虛擬機生成dump文件。
jmap命令格式
相比jstat命令井氢,jmap命令明顯就簡單的多了弦追,就兩個參數(shù):
-
option:選項,jmap主要提供以下選項:
-dump
生成Java堆內(nèi)存快照花竞,使用格式為:-dump:[live, ]format=b,file=<filename>
劲件,使用hprof二進(jìn)制形式,輸出jvm的heap內(nèi)容到文件<filename>约急,live子參數(shù)是可選的零远,如果指定live選項,就只dump出存活的對象厌蔽;finalizerinfo
顯示在F-Queue中等待Finalizer線程執(zhí)行finalize方法的對象牵辣;-heap
顯示heap詳細(xì)信息,比如使用哪種回收器奴饮、參數(shù)配置纬向、分代狀態(tài)等;histo
顯示每個class的實例數(shù)目拐云,內(nèi)存占用罢猪,類全名信息。VM的內(nèi)部類名字開頭會加上前綴“*”.叉瘩,如果帶上live子參數(shù)膳帕,則只統(tǒng)計活的對象數(shù)量;-permstat
以ClassLoader為統(tǒng)計口徑顯示永久代內(nèi)存狀態(tài)薇缅,需要注意的是危彩,JDK 8將該option替換成了-clstats
;-F
強制生成dump文件泳桦,當(dāng)虛擬機進(jìn)程對-dump
選項沒有響應(yīng)時可以使用汤徽。
pid:需要生成dump文件的進(jìn)程id
dump文件在這里我就不做演示了,給一個簡單的使用吧:
從輸出的結(jié)果可以清晰的看出每一個class的實例數(shù)目以及內(nèi)存占用情況灸撰。
jstack
用戶生成虛擬機當(dāng)前時刻的線程快照(threaddump/javacore文件)谒府。線程快照就是當(dāng)前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧集合,生成線程快照的目的也就是為了定位線程出現(xiàn)長時間卡頓的原因浮毯。
jstack命令格式
跟jmap命令一樣完疫,jstack命令也只有兩個參數(shù):
-
option:選項,jstack主要提供以下選項:
-F
當(dāng)線程出現(xiàn)長時間卡頓的時候债蓝,強制輸出線程堆棧壳鹤;-l
除堆棧外,顯示關(guān)于鎖的附加信息饰迹;-m
如果調(diào)用JNI方法芳誓,可以顯示C/C++的堆棧余舶。
pid:需要生成threaddump的進(jìn)程id。
簡單給一個使用吧還是:
從輸出結(jié)果可以清晰的看到線程堆棧以及鎖相關(guān)信息锹淌。具體的怎么根據(jù)threaddump分析定位問題最近暫時沒有遇到匿值,等遇到了再出文詳細(xì)介紹啦~
jhat
與jmap命令搭配使用,分析jmap生成的dump文件葛圃。jhat內(nèi)置一個微型的HTTP/HTML服務(wù)器千扔,生成dump文件的分析結(jié)果后,可以在瀏覽器中進(jìn)行查看库正。其實這個命令在實際生產(chǎn)中使用比較少曲楚,為什么不用的原因我總結(jié)下來大概有兩點:第一呢,線上生產(chǎn)環(huán)境怎么可能允許你在線上機器直接分析dump文件啊~~~第二就是jhat的分析功能相比其他工具簡直是太簡陋了褥符。
它的使用也很簡單啊龙誊,jhat <dump文件名稱>
,等分析完就打開瀏覽器訪問相應(yīng)的地址+端口就可以愉快的開始分析dump文件了喷楣。我在這里就不再做詳細(xì)使用實例分析啦趟大,有興趣的小伙伴們請自行嘗試~
寫在最后面
本文只是粗糙的介紹了怎么使用命令排查JVM問題,但是小伙伴們一定不要以為會命令就會排查了铣焊,一定要親自擼起袖子查幾輪問題才能熟練理解掌握逊朽,當(dāng)然我也會在后續(xù)出一些我排查線上問題的小文章啦~歡迎大家持續(xù)關(guān)注呀。