天天看點

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

【JVM性能優化】問題故障排查的解決方案(上)

前提概要

線上故障主要會包括cpu、磁盤、記憶體以及網絡問題,而大多數故障可能會包含不止一個層面的問題,是以進行排查時候盡量四個方面依次排查一遍。同時例如jstack、jmap等工具也是不囿于一個方面的問題的,基本上出問題就是df、free、top三連,然後依次jstack、jmap伺候,具體問題具體分析即可。

CPU的問題

一般來講我們首先會排查cpu方面的問題。cpu異常往往還是比較好定位的。原因包括業務邏輯問題(死循環)、頻繁gc以及上下文切換過多。而最常見的往往是業務邏輯(或者架構邏輯)導緻的,可以使用jstack來分析對應的堆棧情況。

jstack分析cpu問題

  1. 先用ps指令找到對應程序的pid(如果你有好幾個目标程序,可以先用top看一下哪個占用比較高),來找到cpu使用率比較高的一些線程
top -H -p pid

這裡需要注意的是 -p代表着通過程序号,-H 查詢的是輸出使用率最高線程

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  1. 将占用最高的pid轉換為16進制得到nid
printf ‘%x\n’ pid
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  1. 接着直接在jstack中找到相應的堆棧資訊
jstack ‘0x42’ | grep ‘nid’ -C5 –color

可以看到我們已經找到了nid為0x42的堆棧資訊,接着隻要仔細分析一番即可。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  1. 排查整個jstack檔案
  • 當然更常見的是我們對整個jstack檔案進行分析,通常我們會比較關注WAITING和TIMED_WAITING的部分,BLOCKED就不用說了。
  • 使用指令

    cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c

    來對jstack的狀态有一個整體的把握,如果WAITING之類的特别多,那麼多半是有問題啦。
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

JVM頻繁gc(FullGC)

使用jstack來分析問題,但有時候我們可以先确定下gc是不是太頻繁,使用

jstat -gc pid 1000

指令來對gc分代變化情況進行觀察,1000表示采樣間隔(ms)。

  • S0C/S1C、S0U/S1U、EC/EU、OC/OU、MC/MU分别代表兩個Survivor區、Eden區、老年代、中繼資料區的容量和使用量。
  • YGC/YGT、FGC/FGCT、GCT則代表YoungGc、FullGc的耗時和次數以及總耗時。
如果看到gc比較頻繁,再針對gc方面做進一步分析
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

上下文切換

針對頻繁上下文問題,可以使用vmstat指令來進行檢視。

vmstat 1

代表着1秒列印一次。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  • cs(context switch)一列則代表了上下文切換的次數。

如果我們希望對特定的pid進行監控那麼可以使用

pidstat -w pid

指令,cswch和nvcswch表示自願及非自願切換。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

磁盤的問題

狀态資訊

  • 磁盤問題和cpu一樣是屬于比較基礎的。首先是磁盤空間方面,我們直接使用

    df -hl

    來檢視檔案系統狀态
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  • 磁盤問題還是性能上的問題。可以通過

    iostat -d -k -x

    來進行分析。
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

圖檔來源于blog.csdn.net/pengjunlee/…

  • 最後一列%util可以看到每塊磁盤寫入的程度,而rrqpm/s以及wrqm/s分别表示讀寫速度,一般就能幫助定位到具體哪塊磁盤出現問題了。
  • 另外我們還需要知道是哪個程序在進行讀寫,一般來說開發自己心裡有數,或者用

    iotop

    指令來進行定位檔案讀寫的來源。
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

不過這邊拿到的是tid,我們要轉換成pid,可以通過readlink來找到pidreadlink -f /proc/*/task/tid/…/…。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

圖檔來源于blog.csdn.net/pengjunlee/…

找到pid之後就可以看這個程序具體的讀寫情況cat /proc/pid/io

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

可以通過lsof指令來确定具體的檔案讀寫情況

lsof -p pid

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

圖檔來源于blog.csdn.net/pengjunlee/…

記憶體問題

記憶體問題排查起來相對比CPU麻煩一些,場景也比較多。主要包括OOM、GC問題和堆外記憶體。一般來講,我們會先用

free

指令先來檢查一發記憶體的各種情況。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

堆内記憶體

記憶體問題大多還都是堆内記憶體問題。表象上主要分為OOM和StackOverflow。

OOM問題

JVM中的記憶體不足,OOM大緻可以分為以下幾種:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
複制代碼
           
  • 沒有足夠的記憶體空間給線程配置設定java棧,基本上還是線程池代碼寫的有問題,比如說忘記shutdown,或者使用無限制的任務隊列,是以說應該首先從代碼層面來尋找問題,使用jstack或者jmap。
  • 如果一切都正常,JVM方面可以通過指定Xss來減少單個thread stack的大小。
  • 另外也可以在系統層面,可以通過修改/etc/security/limits.conf的,nofile和nproc來增大os對線程的限制
問題
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
複制代碼
           
原因

這個意思是堆的記憶體占用已經達到-Xmx設定的最大值,應該是最常見的OOM錯誤了。

解決辦法
解決思路仍然是先應該在代碼中找,懷疑存在記憶體洩漏,通過jstack和jmap去定位問題。如果說一切都正常,才需要通過調整Xmx的值來擴大記憶體。
問題
Caused by: java.lang.OutOfMemoryError: Meta space
複制代碼
           
解決辦法

這個意思是中繼資料區的記憶體占用已經達到

-XX:MaxMetaspaceSize

設定的最大值,排查思路和上面的一緻,參數方面可以通過

-XX:MaxPermSize

來進行調整(這裡就不說1.8以前的永久代了)。

問題
Exception in thread "main" java.lang.StackOverflowError
複制代碼
           
解決辦法
表示線程棧需要的記憶體大于Xss值,同樣也是先進行排查,參數方面通過Xss來調整,但調整的太大可能又會引起OOM。
  • 使JMAP定位代碼記憶體洩漏,上述關于OOM和StackOverflow的代碼排查方面,我們一般使用

    jmap -dump:format=b,file=filename pid

    來導出dump檔案
    【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  • 通過MAT(Eclipse Memory Analysis Tools)導入dump檔案進行分析,記憶體洩漏問題一般我們直接選Leak Suspects即可,mat給出了記憶體洩漏的建議。另外也可以選擇Top Consumers來檢視最大對象報告。
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
  • 線程相關的問題可以選擇thread overview進行分析。除此之外就是選擇Histogram類概覽來自己慢慢分析,大家可以搜搜mat的相關教程。

代碼産生記憶體洩漏是比較常見的事,并且比較隐蔽,需要開發者更加關注細節。

  • 比如說每次請求都new對象,導緻大量重複建立對象;
  • 進行檔案流操作但未正确關閉;手動不當觸發gc,ByteBuffer緩存配置設定不合理等都會造成代碼OOM。

可以在啟動參數中指定

-XX:+HeapDumpOnOutOfMemoryError

來儲存OOM時的dump檔案。

gc問題和線程

  • GC問題除了影響cpu也會影響記憶體,排查思路也是一緻的。一般先使用jstat來檢視分代變化情況,比如youngGC或者fullGC次數是不是太多呀;EU、OU等名額增長是不是異常呀等。
  • 線程的話太多而且不被及時gc也會引發oom,大部分就是之前說的unable to create new native thread。除了jstack細細分析dump檔案外,我們一般先會看下總體線程,通過

    pstree -p pid |wc -l

    【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

或者直接通過檢視/proc/pid/task的數量即為線程數量。

堆外記憶體

如果碰到堆外記憶體溢出,那可真是太不幸了。首先堆外記憶體溢出表現就是實體常駐記憶體增長快,報錯的話視使用方式都不确定。
由于使用Netty導緻的,那錯誤日志裡可能會出現OutOfDirectMemoryError錯誤,如果直接是DirectByteBuffer,那會報OutOfMemoryError: Direct buffer memory。

堆外記憶體溢出往往是和NIO的使用相關,一般我們先通過

pmap

來檢視下程序占用的記憶體情況

pmap -x pid | sort -rn -k3 | head -30

,這段意思是檢視對應pid倒序前30大的記憶體段。這邊可以再一段時間後再跑一次指令看看記憶體增長情況,或者和正常機器比較可疑的記憶體段在哪裡。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

我們如果确定有可疑的記憶體端,需要通過

gdb

來分析

gdb --batch --pid {pid} -ex "dump memory filename.dump {記憶體起始位址} {記憶體起始位址+記憶體塊大小}"

排查問題

擷取dump檔案後可用heaxdump進行檢視

hexdump -C filename | less

,不過大多數看到的都是二進制亂碼。

NMT是Java7U40引入的HotSpot新特性,配合jcmd指令我們就可以看到具體記憶體組成了。需要在啟動參數中加入

-XX:NativeMemoryTracking=summary

或者

-XX:NativeMemoryTracking=detail

,會有略微性能損耗。

一般對于堆外記憶體緩慢增長直到爆炸的情況來說,可以先設一個基線

jcmd pid VM.native_memory baseline

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

然後等放一段時間後再去看看記憶體增長的情況,通過

jcmd pid VM.native_memory detail.diff(summary.diff)

做一下summary或者detail級别的diff。

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:
【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

可以看到jcmd分析出來的記憶體十分詳細,包括堆内、線程以及gc(是以上述其他記憶體異常其實都可以用nmt來分析),這邊堆外記憶體我們重點關注Internal的記憶體增長,如果增長十分明顯的話那就是有問題了。

detail級别的話還會有具體記憶體段的增長情況,如下圖

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後:

此外在系統層面,我們還可以使用strace指令來監控記憶體配置設定

strace -f -e "brk,mmap,munmap" -p pid

這邊記憶體配置設定資訊主要包括了pid和記憶體位址

不過其實上面那些操作也很難定位到具體的問題點,關鍵還是要看錯誤日志棧,找到可疑的對象,搞清楚它的回收機制,然後去分析對應的對象。

  • 比如DirectByteBuffer配置設定記憶體的話,是需要full GC或者手動system.gc來進行回收的(是以最好不要使用-XX:+DisableExplicitGC)。

那麼其實我們可以跟蹤一下DirectByteBuffer對象的記憶體情況,通過jmap -histo:live pid手動觸發fullGC來看看堆外記憶體有沒有被回收。

如果被回收了,那麼大機率是堆外記憶體本身配置設定的太小了,通過

-XX:MaxDirectMemorySize

進行調整。如果沒有什麼變化,那就要使用jmap去分析那些不能被gc的對象,以及和DirectByteBuffer之間的引用關系了。

最後:

我想,可能還有很多人在今年剛過去的金三銀四春招中保持着觀望的形勢,害怕自己的能力不夠,或者是安于現狀,覺得目前拿着幾千的月薪覺得能夠接受,那麼你就要注意了,這是非常危險的!

我們身為技術人員,最怕的就是安于現狀,一直在原地踏步,那麼你可能在30歲就會迎來自己的職業危機,因為你工作這麼久提升的隻有自己的年齡,技術還是萬年不變!

如果你想在未來能夠自我突破,圓夢大廠,那或許以上這份Java學習資料,你需要閱讀閱讀,希望能夠對你的職業發展有所幫助。

擷取方式: 隻需你**點贊+關注**後,加入Java架構資源交流群,找管理者擷取哦-!

為你工作這麼久提升的隻有自己的年齡,技術還是萬年不變!

如果你想在未來能夠自我突破,圓夢大廠,那或許以上這份Java學習資料,你需要閱讀閱讀,希望能夠對你的職業發展有所幫助。

擷取方式: 隻需你**點贊+關注**後,加入Java架構資源交流群,找管理者擷取哦-!

【JVM性能優化】問題故障排查的解決方案(上)【JVM性能優化】問題故障排查的解決方案(上)前提概要CPU的問題磁盤的問題最後: