概述
最近遇到線上故障,具體的情況就是后端服務請求一直 pending,服務經常假死重啟。 但是觀察 整個進程CPU + 內存消耗不是特別大, 沒有明顯的資源泄漏情況。
故障回溯
-
top -p 40872
查看進程情況,發現沒有明顯的 內存和 CPU使用率過高
-
top -Hp 40872 查看進程下的所有線程,沒有明顯的線程 CPU + 內存使用率過高
備注若遇到 某個線程 資源消耗非常高,可以采取把對應的 線程id轉換為十六進制 , 然后在 線程 dump文件查詢具體的行為。
printf "%x" 進程號 -
到此步驟基本確定沒有明顯的內存泄漏和 線程死鎖故障。但是還是得看下 GC 的情況, 因為經常的 FGC 會影響服務的穩定性,導致服務假死。
打印類加載器信息 執行命令 jmap -clstats 36001
此處主要觀察 是否加載了很多自己的業務 class, 若是則需要考慮是否大量違規使用反射 ;需要調優甚至調大元數據區
顯示堆中對象的統計信息 jmap -histo:live 36001
也是執行觀察整個容器中的 對象的分布情況, 主要觀察是否有非常多余的業務對象,若非常多需要考慮優化;
比如很多業務對象是會很快就銷毀的, 需要考慮增大 Eden區, 盡量在 YGC時釋放掉,避免堆積到 old觸發 FGC 。若這些對象就是比如緩存等, 則需要長時間保存,可以適度考慮調大 old區,減少 FGC的次數。
堆棧掃描 jmap -heap 36001 > ./jmapheap.txt 總體來說這個命令非常有用 可以馬上發現是否有明顯的內存泄漏 ;
如下圖所示基本是健康的, 整個 Eden Space、From Space、To Space、PS Old Generation 占用情況還比較合理,沒有明顯的占滿情況。
生成堆轉儲快照dump文件,以hprof二進制格式轉儲Java堆到指定filename的文件中
jmap -dump:format=b,file=./jmapdump2.hprof 6230 以上文件使用 jhat(Java堆分析工具) 分析
以上文件可以 在 https://gceasy.io 工具中直接上傳分析, 本人在使用過程中發現太大沒法打開,后續可以考慮減少導出的大小
生成 dump文件 一共2種方式:
其一,通過上面介紹的 jmap工具生成,可以生成任意一個java進程的dump文件;
其二,通過配置JVM參數生成,選項“-XX:+HeapDumpOnOutOfMemoryError ”和-“XX:HeapDumpPath”所代表的含義就是當程序出現OutofMemory時,將會在相應的目錄下生成一份dump文件,而如果不指定選項“XX:HeapDumpPath”則在當前目錄下生成dump文件。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/biapp/m.hprof
接下來分析下實時的 GC情況;具體命令可以參考 https://www.cnblogs.com/yjd_hycf_space/p/7755633.html
打印 進程的 GC 明細 jstat -gc 6230 3000 這個命令呈現的具體的大小單位 KB
jstat -gcutil 30996 3000 這個命令展示是每個區域占用百分比
以上可以發現元數據區 已經快占滿了, 觸發了了 5次 FGC , 元數據是有個初始大小的 默認是 28MB , 當超過時會觸發 FGC 回收掉不需要的 class, 然后回收的比例調節上限,所有若知道當前業務占用比較大的元數據區,可以直接設置一個比較大的元數據區
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m 減少FGC的次數
改為之后運行下述命令觀察 , 發現 FGC 已經去除了, 證明了猜想
//只查看年輕代的情況
jstat -gcnew 40872 1000 整個年輕代也是運行正常的
以上基本發現 內存 、GC 沒啥問題,故考慮分析下線程堆棧:
執行如下 命令 jstack 12994 > ./jstack.txt
:OpenJDK提供的庫,用於查看Java對象的內存布局,這個很有用,可以借助它來跟蹤鎖升級等過程:
openJDK源碼:查看 JDK native 方法的實現
strace:跟蹤程序運行過程發起的系統調用
https://fastthread.io:線程棧分析的網站