JAVA應(yīng)用生產(chǎn)問題排查步驟
學(xué)會(huì)這篇文章里面的命令并熟練使用,出去面試就可以說自己有5年工作經(jīng)驗(yàn)并且精通JVM了。本篇文章中介紹的命令絕對(duì)是JAVA程序員平時(shí)工作中經(jīng)常使用的并且必須會(huì)的命令,如果你不會(huì)你就是沒有工作經(jīng)驗(yàn)的人。如果你不會(huì),那么在別人眼中你肯定就是菜鳥一個(gè),所以本篇文章中的命令,你必須學(xué)會(huì)并且熟練使用。
注意:本篇文章內(nèi)容基于JDK版本:
java version “1.6.0_113”
Java™ SE Runtime Environment (build 1.6.0_113-b01)
Java HotSpot™ 64-Bit Server VM (build 20.111-b01, mixed mode)
1 JAVA自帶命令JPS
jps這個(gè)命令在JDK的安裝目錄bin/下面
使用jps這個(gè)命令可以找到運(yùn)行在服務(wù)器(電腦)上面的所有JVM進(jìn)程。jps最常用、最有用的命令有以下倆個(gè)。
- jps -vljps -vl這個(gè)命令中的-v是輸出啟動(dòng)JVM進(jìn)程時(shí)傳遞給JVM的參數(shù),-l是輸出main方法所在類的完整路徑或者JAR包的完成路徑,命令結(jié)果如下截圖:
- jps -mljps -ml這個(gè)命令中的-m是輸出傳給main方法的參數(shù),-l是輸出main方法所在類的完整路徑或者JAR包的完成路徑,命令結(jié)果如下截圖:在Liunx服務(wù)器上面執(zhí)行jps -ml結(jié)果如下截圖:工作中最常用的命令是jps -vl。jps -ml這個(gè)命令結(jié)果比較雞肋并且不常用。你可以自己寫一個(gè)main方法,在main方法里面寫一個(gè)死循環(huán),并且傳遞給main方法幾個(gè)參數(shù),然后再執(zhí)行一下jps -vl和jps -ml這倆個(gè)命令感受一下。你如果光看我的文章,自己不手敲這些命令,你就是瞎看,沒一點(diǎn)用。
如果你們公司是通過weblogic部署的應(yīng)用,那么weblogic啟動(dòng)的時(shí)候會(huì)給JVM進(jìn)程傳一個(gè)參數(shù)-Dweblogic.Name= yourwebname ,這個(gè) yourwebname 就是你部署應(yīng)用的應(yīng)用名稱,然后你可以通過jps -vl |grep yourwebname 這個(gè)命令找到你javaweb項(xiàng)目的jvm進(jìn)程id。
但是,如果你的應(yīng)用是通過Tomcat部署,那么jps -vl |grep yourwenname 這個(gè)命令就不太管用了。不過jps -vl 這個(gè)命令肯定還是管用的,因?yàn)閖ps是java自帶的命令,跟tomcat或者weblogic沒有任何關(guān)系。
如果你的應(yīng)用部署在tomcat上面,那么你就用jps -vl 找tomcat這個(gè)JVM進(jìn)程就可以了。原理如下:
一個(gè)tomcat就只能啟動(dòng)一個(gè)JVM進(jìn)程,如果你的tomcat里面部署了多個(gè)Java Web應(yīng)用, 那么你的多個(gè)Java Web應(yīng)用共享這個(gè)一個(gè)JVM,每個(gè)JavaWeb應(yīng)用都有自己的ClassLoader,也就是說一個(gè)JVM中可以有多個(gè)Class Loader。
如果其中一個(gè)JavaWeb應(yīng)用發(fā)生了內(nèi)存泄漏或者別的原因把JVM搞垮了,那么這個(gè)tomcat下面的所有JavaWeb應(yīng)用就全掛了,所以生產(chǎn)環(huán)境沒有人會(huì)用一個(gè)tomcat去部署多個(gè)JavaWeb應(yīng)用的。生產(chǎn)環(huán)境,都是啟動(dòng)多個(gè)tomcat,每個(gè)tomcat只部署一個(gè)JavaWeb應(yīng)用。
我感覺一個(gè)tomcat只能啟動(dòng)一個(gè)JVM進(jìn)程這個(gè)設(shè)計(jì)非常不合理,假如我一個(gè)tomcat下面部署了多個(gè)JavaWeb應(yīng)用,我想給每個(gè)JavaWeb應(yīng)用設(shè)置不同的JVM參數(shù)都不行。現(xiàn)在的SpringBoot應(yīng)用都是直接通過java -jar App.jar啟動(dòng)的,并且SpringBoot直接就內(nèi)置了一個(gè)tomcat,所以你通過java -jar app.jar啟動(dòng)的時(shí)候就是一個(gè)tomcat只部署一個(gè)We應(yīng)用。
關(guān)于tomcat的知識(shí)我是參考CSDN網(wǎng)站上面的大神greencacti的文章: Tomcat中會(huì)啟動(dòng)一個(gè)jvm還是多個(gè)jvm? 。
還有CSDN網(wǎng)站上面的大神tyyh08的文章
一個(gè)tomcat部署多個(gè)應(yīng)用,有幾個(gè)JVM? 。
2 Liunx上面top命令
使用jps命令找到我們的jvm進(jìn)程ID之后趕緊使用top命令看一下服務(wù)器上面的大致情況,top命令默認(rèn)好像是3秒鐘刷新一次。
top 命令結(jié)果如下截圖:
top命令結(jié)果顯示之后按小寫字母c,可以顯示完整的命令行,這個(gè)非常有用,一定要用。如截圖
然后按大寫母P,以CPU使用率排序(按進(jìn)程負(fù)載排序,排在最上面的就是CPU使用率最高的)
按大寫字母M,以內(nèi)存使用率排序
高亮顯示:可以這樣搞,先按大寫字母P,再按小寫字母b,再按小寫字母x。效果如下截圖:
按鍵b打開或關(guān)閉 運(yùn)行中進(jìn)程的高亮效果
按鍵x打開或關(guān)閉 排序列的高亮效果
按數(shù)字 1 鍵,可以顯示多個(gè)CPU的使用情況
top顯示的是服務(wù)器上面所有的進(jìn)程概況,top類似于windows電腦上面的的任務(wù)管理器。
但是,如果我只關(guān)心我自己的JVM進(jìn)程呢?可以使用這個(gè)命令
top -p jvm進(jìn)程ID,這個(gè)命令可以指定進(jìn)程,只顯示指定進(jìn)程的概況,按空格鍵可以立即刷新。
top -d 2 -p jvm進(jìn)程ID,這個(gè)命令可以指定進(jìn)程,只顯示指定進(jìn)程的概況,-d 2的意思是倆秒鐘刷新一次。
top -Hp 進(jìn)程ID,這個(gè)命令可以顯示指定進(jìn)程下面的線程信息, 這個(gè)超級(jí)有用,必須要會(huì)。 而且這個(gè)命令搭配JAVA的自帶命令jstack可以非常快速并且有效的定位代碼問題。
然后使用linux的自帶命令,printf將線程ID轉(zhuǎn)換成16進(jìn)制,printf “0x%xn” 19235
然后使用JAVA的自帶命令jstack去找到這個(gè)線程ID都在干什么就行了。這幾個(gè)命令非常非常重要。
使用top命令,其實(shí)主要關(guān)注top命令里面的RES列的值,%CPU列的值,%MEM列的值,這三列的值就行了。然后
拿RES列的值跟后面jmap命令顯示的jvm堆的值做比較。如果RES的值,比你的Xmx的值還大, 注意是比你設(shè)置的Xmx的值還大 ,說明你的java程序引用的有非堆(堆外地址)內(nèi)存,比如NIO,DirectByteBuffer這些類會(huì)使用堆外的內(nèi)存。所以要注意堆外內(nèi)存泄露情況(就是你代碼里面雖然已經(jīng)不用這塊堆外地址了,但是你的引用沒釋放,導(dǎo)致你的程序浪費(fèi)了很多用不到的堆外地址)。 堆外內(nèi)存泄漏可以使用google出品的perf工具來排查。perf工具使用參見,大神的文章 feininan 的文章《 使用google perf工具來排查堆外內(nèi)存占用 》。
這塊知識(shí)點(diǎn)摘自網(wǎng)絡(luò)上面 MartinDai 大神的文章 記一次堆外內(nèi)存泄漏排查過程 。 執(zhí)行top命令,再按c,看到對(duì)應(yīng)的進(jìn)程所占用的RES有8個(gè)多G(這里當(dāng)時(shí)忘記截圖了),但是實(shí)際上我們配置的Xmx只有3G,而且程序還是正常運(yùn)行的,所以不會(huì)是堆占用了這么多,于是就把問題方向指向了非堆的內(nèi)存。
RES列和%MEM列的關(guān)系為:RES/總內(nèi)存=%MEM,我們來算一下:
從上圖可以看到,這臺(tái)服務(wù)器的物理內(nèi)存(運(yùn)存)為:132024628k = 126G.
32924這個(gè)進(jìn)程的RES列的值為:5.3G
32924這個(gè)進(jìn)程的%MEM列的值為:4.2%
我們來算一下:RES:5.3G / total Mem:126G = 0.042 = 4.2%
這下你明白了吧RES列和%MEM列,表達(dá)的其實(shí)是一個(gè)意思。
RES的意思是:Resident Memory Size 常駐內(nèi)存大小。使用man top命令看下top命令的官方幫助文檔怎么說:
TIPS:按小寫字母q可以退出top命令
JVM進(jìn)程的RES列的值和JVM的heap(堆)的關(guān)系也很有意思,RES列的值代表JVM進(jìn)程在運(yùn)行過程中曾經(jīng)使用過的最大內(nèi)存, 注意是曾經(jīng)使用過的最大內(nèi)存 ,并不代表此時(shí)此刻JVM堆使用的內(nèi)存大小。舉個(gè)例子,假如JVM在業(yè)務(wù)高峰期有大量請(qǐng)求進(jìn)來,此時(shí)堆內(nèi)存使用量肯定會(huì)上升,假如此時(shí)堆內(nèi)存的使用量為5G,然后觸發(fā)了JVM的垃圾回收,垃圾回收之后JVM堆內(nèi)存使用量下降到了1G,此時(shí)你用top命令去看這個(gè)JVM進(jìn)程的RES列的值有很大可能還是5G,原因就是這5G內(nèi)存目前還歸屬JVM進(jìn)程管理并使用,JVM 可能不會(huì)立即 把回收掉的堆空間還給操作系統(tǒng)。
只要RES列的值不大于我們給JVM設(shè)置Xmx的值,就沒有關(guān)系。Xmx這個(gè)參數(shù)的意思就是告訴操作系統(tǒng),JVM本大爺我有可能要使用Xmx這么大的內(nèi)存,你操作系統(tǒng)提前做好心理準(zhǔn)備。JVM進(jìn)程剛運(yùn)行的時(shí)候并不會(huì)直接找操作系統(tǒng)要Xmx這么大的內(nèi)存,JVM在運(yùn)行的過程中根據(jù)自己的情況一點(diǎn)一點(diǎn)問操作系統(tǒng)申請(qǐng)的。一旦JVM進(jìn)程從操作系統(tǒng)申請(qǐng)到內(nèi)存之后,JVM在運(yùn)行的過程中就可能就不會(huì)還給操作系統(tǒng)了。因?yàn)镴VM跟操作系統(tǒng)之間如果總是借呀還呀的就會(huì)很浪費(fèi)性能,沒有什么必要。JVM進(jìn)程退出之后占用的這些內(nèi)存肯定會(huì)釋放給操作系統(tǒng)的。關(guān)于JVM是否把空閑的堆(heap)內(nèi)存還給操作系統(tǒng)這件事,JVM提供了一些參數(shù):
-XX:MinHeapFreeRatio、
-XX:MaxHeapFreeRatio、
-XX:-ShrinkHeapInSteps
這幾個(gè)參數(shù)你們自己查資料吧。
怎么驗(yàn)證這個(gè)說法?先找一個(gè)運(yùn)行了1天以上的JVM進(jìn)程,然后使用top命令看下這個(gè)JVM進(jìn)程的RES列的值,然后再用我后面介紹的jmap -heap 19463 命令看一下JVM進(jìn)程堆的使用情況就知道了。
這塊知識(shí)點(diǎn)摘自掘金APP上面 空無 大神的文章《 運(yùn)維:你們 JAVA 服務(wù)內(nèi)存占用太高,還只增不減!告警了,快來接鍋 》。
熊峰 大神的文章《 JVM調(diào)優(yōu)之探索CMS和G1的物理內(nèi)存歸還機(jī)制 》。
stackoverflow 網(wǎng)站的提問《 Does GC release back memory to OS? 》。
Oracle官方文檔《 Default Option Values for Heap Size 》。
3 JAVA自帶命令–jstat,查看GC(垃圾回收情況)
jstat這個(gè)命令在JDK的安裝目錄bin/下面
3.1 使用 jstat 查看 GC(垃圾回收) 的情況
jstat -gc jvm進(jìn)程PID 2000
這個(gè)命令會(huì)每隔2秒統(tǒng)計(jì)一下JVM進(jìn)程(PID):19463的垃圾收集情況,命令最后2000的意思就是每隔2秒統(tǒng)計(jì)一次。使用jstat命令可以實(shí)時(shí)監(jiān)測(cè)到Y(jié)GC和FGC的情況,包括每次YGC和FGC各花費(fèi)了多長(zhǎng)時(shí)間,到目前為止總共進(jìn)行了多少次YGC和FGC。
jstat -gcutil jvm進(jìn)程PID 2000
這個(gè)命令里面的-gcutil 監(jiān)視內(nèi)容與-gc基本相同,但輸出主要關(guān)注已使用空間占總空間的百分比 。
也可以寫成 jstat -gcutil jvm進(jìn)程PID 2s , 2s也是2秒鐘刷新一次的意思。還有一種寫法是 jstat -gcutil 19463 2s 5 ,這個(gè)命令是2秒鐘刷新一次,總共統(tǒng)計(jì)5次就行了,5次之后自動(dòng)結(jié)束jstat命令,不需要你手工按crtl+c去終止命令。
–gcutil監(jiān)視內(nèi)容 與 -gc基本相同,但輸出主要關(guān)注已使用空間占總空間的 百分比 。
jstat -gccause 28549 2000
gccause 與-gcutil功能一樣,但是會(huì)額外輸出導(dǎo)致上一次GC產(chǎn)生的原因
工作中我個(gè)人使用jstat -gccause和jstat -gc這個(gè)倆個(gè)命令比較多。
3.2 jstat輸出內(nèi)容解釋
- jstat -gc jvm進(jìn)程PID 2000
jvm的堆(heap)空間由S0(Survivor,0號(hào)幸存區(qū))+S1(Survivor,1號(hào)幸存區(qū))+Eden(年輕代)+Tenured(Old老年代)+Permanent(永久代)組成的。
注意:Permanent(永久代)在jdk1.7還是jdk1.8的時(shí)候被移除了,換成Metaspace(元數(shù)據(jù))了。注意,永久代的意思并不是這塊內(nèi)存永遠(yuǎn)不會(huì)回收,在發(fā)生FullGC的時(shí)候,永久代里面的垃圾也會(huì)被回收掉。
所以jstat的輸出結(jié)果說明為:
S0C(Capacity):S0的最大內(nèi)存,總內(nèi)存。Capacity就是容量的意思。單位:kb
S0U(Used):S0目前已經(jīng)使用的大小。Used就是已經(jīng)使用的意思。單位:kb
EC,EU:就是年輕代
PC,PU:就是永久代
OC,OU:就是老年代
YGC:就是年輕代的GC次數(shù)
YGCT:就是年輕代GC所花費(fèi)的時(shí)間,單位秒
FGC:就是FGC的次數(shù)
FGCT:就是FGC所花費(fèi)的時(shí)間,單位秒
GCT:就是YGC+FGC倆個(gè)GC加起來所花費(fèi)的時(shí)間,單位秒
- jstat -gcutil 4777 2000 5
這個(gè)命令里面的-gcutil 監(jiān)視內(nèi)容與-gc基本相同,但輸出主要關(guān)注已使用空間占總空間的百分比,所以-gcutil看到的是使用率。
這些命令你會(huì)使用了,關(guān)鍵結(jié)果你能看得懂嗎?其實(shí)很簡(jiǎn)單,我們主要關(guān)注,年輕代和老年代和持久代的使用率,目前用了多少G,最大的堆內(nèi)存空間配置的是多少?是不是快滿了,是不是快要內(nèi)存溢出了就行。GC前后的年輕代和老年代占用的空間是否減少了,如果發(fā)生了一次GC,年輕代和老年代占用的空間并沒有減少,那說明你的代碼發(fā)生了內(nèi)存泄漏。要趕緊使用我下面介紹的jmap命令將java的堆現(xiàn)場(chǎng)的情況dump下來使用MAT軟件或者GCeasy或者visualVM或者國(guó)內(nèi)PerfMa社區(qū)的軟件來分析dump內(nèi)存文件,找到代碼泄漏的真正原因。Perfma社區(qū)的 阿飛Javaer 大神說FullGC一天超過一次肯定就不正常了,發(fā)現(xiàn)FullGC頻繁的時(shí)候優(yōu)先調(diào)查內(nèi)存泄漏問題。我認(rèn)為這個(gè)說法不太對(duì),我看了一下,我們生產(chǎn)環(huán)境的GC情況,F(xiàn)ullGC一天500次左右,服務(wù)也挺正常的。并且老年代回收完使用率才13%,說明我們生產(chǎn)環(huán)境FullGC是可以把垃圾回收掉的。FullGC的次數(shù)本質(zhì)是跟JVM的內(nèi)存使用量有關(guān)系的,如果你們的系統(tǒng)業(yè)務(wù)很繁忙,FullGC次數(shù)多也是正常的,只有GC之后能把垃圾都回收掉就可以。并且每次FullGC的STW線程停頓時(shí)間不長(zhǎng)也沒有關(guān)系的。
4. JAVA自帶命令–jinfo,查看JVM的配置信息
jinfo這個(gè)命令在JDK的安裝目錄bin/下面
jinfo -flags 12832
這個(gè)命令可以查看我們給JVM設(shè)置的配置項(xiàng)和參數(shù)(默認(rèn)+人工配置)
這個(gè)命令沒啥說的,很簡(jiǎn)單就是看我們給JVM設(shè)置的一些參數(shù)信息。
jinfo -flag MaxPermSize 1919,注意這個(gè)命令flag后面沒有帶s,這個(gè)命令用來看我們有沒有給JVM設(shè)置MaxPermSize這個(gè)參數(shù)。如果有,就將設(shè)置的值顯示出來。
可以看到我,我給JVM設(shè)置的永久代MaxPermSize的最大空間為:-XX:MaxPermSize=1073741824(1個(gè)G)
jinfo -flag HeapDumpPath 111552
jinfo -flag MetaspaceSize 111552
jinfo -flag 這個(gè)命令不常用,最常用的就是 jinfo -flags 這個(gè)帶s的直接看所有的JVM配置。
使用jinfo命令可以看到我們指定的-Xmx 堆的最大值。這里還有一個(gè)經(jīng)驗(yàn)就是,最好將Xms(jvm堆heap的初始化大小) -Xmx(jvm堆heap的最大值),這個(gè)倆個(gè)的值設(shè)置為一樣的,避免每次垃圾回收完成后JVM重新分配內(nèi)存,可以防止在每次GC后進(jìn)行內(nèi)存重新分配,這塊知識(shí)來自 Perfma社區(qū)的 阿飛Javaer
5. JAVA自帶命令–jmap,查看heap(堆)的內(nèi)存使用情況
jmap這個(gè)命令在JDK的安裝目錄bin/下面
jmap -heap 19463 查看java 堆(heap)使用情況
注意看我的截圖,一定要放大了截圖,然后仔細(xì)看
jmap -histo 19463 查看堆內(nèi)存(histogram)中的對(duì)象數(shù)量及大小
jmap -histo 19463 | head -n100 只顯示前100行
或者使用 jmap -histo:live 19463 | more 查看,使用more命令查看
TIPS:more命令按空格可以翻頁查看,按小寫字母q可以退出more命令。
jmap -histo:live 19463 這個(gè)命令會(huì)先觸發(fā)JVM執(zhí)行GC(垃圾回收),然后再統(tǒng)計(jì)信息。為什么要先觸發(fā)GC呢?因?yàn)檫@個(gè)命令live只統(tǒng)計(jì)活著的對(duì)象。
jmap -dump:format=b,file=9739_jvm_heap.hprof 9739
先解釋一下什么叫做dump。dump的意思是轉(zhuǎn)存儲(chǔ),那什么叫做轉(zhuǎn)存儲(chǔ)呢?轉(zhuǎn)存儲(chǔ)就是將內(nèi)存(運(yùn)存)中的數(shù)據(jù)導(dǎo)出,然后保存(持久化)下來。我們都知道APP(程序)在運(yùn)行的時(shí)候也會(huì)產(chǎn)生一些臨時(shí)數(shù)據(jù)或者APP(程序)在運(yùn)行的時(shí)候也需要臨時(shí)存儲(chǔ)一些數(shù)據(jù)。這些數(shù)據(jù)的存儲(chǔ)都是臨時(shí)性的,一旦APP(程序)運(yùn)行結(jié)束,這些數(shù)據(jù)就都消失了。所以當(dāng)JVM運(yùn)行的過程中出現(xiàn)問題的時(shí)候,注意是JVM在運(yùn)行的時(shí)候出問題了,我們就需要把JVM運(yùn)行時(shí)內(nèi)存(heap堆)的情況dump(轉(zhuǎn)存儲(chǔ)),然后分析一下JVM的heap(堆)上面目前是什么情況?
注意:執(zhí)行該命令的時(shí)候會(huì)將整個(gè)JVM上面的所有線程都暫停,如果你的java堆比較大,比如有10個(gè)G左右,那暫停的時(shí)間可能比較長(zhǎng),有可能長(zhǎng)達(dá)10分鐘,所以在生產(chǎn)環(huán)境慎用這個(gè)命令。或者在生產(chǎn)環(huán)境,先讓運(yùn)維把請(qǐng)求都先負(fù)載到別的機(jī)器上面,再執(zhí)行這個(gè)命令。
執(zhí)行完成后在當(dāng)前目錄就會(huì)產(chǎn)生一個(gè)9739_jvm_heap.hprof 的文件
jmap -dump: live ,format=b,file=heapLive.hprof 9739 如果帶上live會(huì)先觸發(fā)一次GC,GC完則只轉(zhuǎn)存儲(chǔ)活著的對(duì)象。
然后,這個(gè)文件是二進(jìn)制的文件,人肯定是看不懂的。所以,需要借助分析dump文件的工具,可以使用工具來分析:
- 國(guó)內(nèi)PerfMa社區(qū)的XElephant在線工具網(wǎng)站為 https://console.perfma.com/ , https://memory.console.perfma.com/
- GCeasy
網(wǎng)站為 https://gceasy.io/
- Memory Analyzer (MAT)
網(wǎng)站為
https://www.eclipse.org/mat/
MAT這個(gè)工具有一個(gè)MemoryAnalyzer.ini配置文件:找到MemoryAnalyzer.ini文件,該文件里面有個(gè)Xmx參數(shù),該參數(shù)表示最大內(nèi)存占用量,默認(rèn)為1024m,根據(jù)堆轉(zhuǎn)儲(chǔ)文件大小修改該參數(shù)即可。MAT工具要求dump文件的后綴名以.hprof結(jié)尾。B站 PerfMa up主 JVM字節(jié)碼的探索與實(shí)踐應(yīng)用(第一期)直播回放 在第56秒說:如果dump文件太大,MAT也會(huì)打不開。
至于這些分析JAVA dump文件的工具怎么用,你們可以自己去Google一下,我后面也會(huì)再寫一篇關(guān)于這些工具的教程。不過有一點(diǎn)需要注意,如果你dump出來的文件很大,那么上面的工具就都不管用了,比如說你dump出來的文件大小為10G,這么大的文件上面的工具想打開一個(gè)10G的文件也非常困難了。要知道分析dump文件的工具本身也是一個(gè)APP,這個(gè)APP要分析這個(gè)10G的dump文件也需要把這個(gè)10個(gè)G的文件加載到自己的內(nèi)存中去,10G他肯定是不好加載的。這個(gè)時(shí)候你只能使用 jmap -histo 32924 | head -n 60 這個(gè)命令手動(dòng)來查看堆內(nèi)存上面什么對(duì)象最多了,這個(gè)命令的截圖如下:
如果你發(fā)現(xiàn)這個(gè)命令的結(jié)果里面有你熟悉的類,那很大可能就是你項(xiàng)目中這個(gè)類的對(duì)象生成的太多了,并且使用完之后沒有釋放造成了內(nèi)存泄漏,是有可能并不是絕對(duì)的,這個(gè)只能當(dāng)線索去分析,不能當(dāng)證據(jù)去使用。
如果你要看jmap -histo 32924這個(gè)命令的全部結(jié)果,可以使用jmap -histo 32924 > /tmp/java_heap_32924.txt這個(gè)命令,將命令的結(jié)果保存到文件中,然后將文件下載到本地查看。
這塊知識(shí)來自 網(wǎng)絡(luò)上的大神:Hollis Java命令學(xué)習(xí)系列(三)——Jmap
博客園上面的大神:星朝 JVM性能調(diào)優(yōu)實(shí)踐——JVM篇
HeapDump社區(qū)上面的大神Coder的技術(shù)之路 高并發(fā)服務(wù)優(yōu)化篇:詳解一次由讀寫鎖引起的內(nèi)存泄漏
HeapDump社區(qū)上面的大神:Java小能手 記一次線上服務(wù)CPU 100%的處理過程
6. JAVA自帶命令–jstack,查看JVM內(nèi)所有的線程運(yùn)行情況
jstack這個(gè)命令在JDK的安裝目錄bin/下面
這個(gè)命令也比較簡(jiǎn)單,沒啥好講的, 但是非常重要,分析問題時(shí)超級(jí)有用。 命令就是jstack -l JVMPID就行了。這個(gè)命令配合我們前面講的 top -Hp 19235 命令和 printf將線程ID轉(zhuǎn)換成16進(jìn)制 printf “0x%xn” 19235 ,能非常快速定位JAVA程序中運(yùn)行卡頓和緩慢的性能問題。
jstack -l 9739
jstack -l 9739 > /tmp/9739_jvm_thread.dump
將jstack -l 9739的命令的全部輸出結(jié)果都保存到文件里面去,然后再下載的本地或者網(wǎng)上去分析。
打開JAVA線程dump文件
打開JAVA線程dump文件之后,我們就可以利用上面我們講過的 top -Hp 19235 命令和 printf將線程ID轉(zhuǎn)換成16進(jìn)制 printf “0x%xn” 19235 ,拿到最占CPU的線程ID之后,來這個(gè)JAVA線程dump文件里面搜索這個(gè)線程,就知道這個(gè)線程在干嘛了,卡在了哪一行。
grep ‘java.lang.Thread.State’ /tmp/9739_jvm_thread.dump | wc -l
統(tǒng)計(jì)總共有多少線程,線程總數(shù)。線程如果太多肯定是有問題的,至于多少線程算多我也不知道。觀察唄,就跟袁隆平老爺子一樣,你沒事多下地觀察一下水稻是怎么生長(zhǎng)的就知道了,經(jīng)驗(yàn)就是這么來的。你們項(xiàng)目平時(shí)正常運(yùn)行的時(shí)候,你上去統(tǒng)計(jì)一下,等過一段時(shí)間你們項(xiàng)目真出問題了你跟平時(shí)的經(jīng)驗(yàn)對(duì)比一下就小蔥拌豆腐一清二白了。
grep “java.lang.Thread.State” /tmp/20210713_thread.log |sort| uniq -c | sort -nr
grep “java.lang.Thread.State”
/tmp/20210713_vhlthread.log |sort| uniq -c | sort -nr 使用這個(gè)命令看下所有的線程都處于什么狀態(tài)
上面介紹的命令使用起來畢竟太原始,效率太低了,不是人干的事。區(qū)分一個(gè)程序猿到底猴子還是人,主要看他會(huì)不會(huì)使用工具。介紹倆個(gè)分析線程快照的工具:
- fastThread
網(wǎng)站為 https://fastthread的具體使用教程,你們自己網(wǎng)上Google吧。.io/
分析結(jié)果
fastthread的具體使用教程,你們自己網(wǎng)上Google吧。
- 國(guó)內(nèi)HeapDump社區(qū)的XSheepdog在線工具網(wǎng)站為 https://www.perfma.com/ , https://console.heapdump.cn/
更多用法需要你自己動(dòng)手去挖掘。
如果您覺得文章對(duì)您有用,請(qǐng)轉(zhuǎn)發(fā)、評(píng)論謝謝~
原文鏈接:
https://www.tuicool.com/articles/Ej6neiE