大家好,我是陶朱公Boy,
背景

不知道大家看到這條告警內容后,是什么感觸?我當時是一臉懵逼的,一萬個為什么縈繞心頭,

什么是CmsGc?CmsGc太頻繁又是什么意思?什么情況下會觸發CMSGC太頻繁這種告警?要怎么樣去找到那個被頻繁創建的物件?最后又需要怎么規避?
接下來這篇文章我會來回答一下:什么是CMSGC太頻繁;整個排查程序與你分享;最后我們一起探討一下一些規避手段,
什么是CMSGC太頻繁
首先我覺得還是有必要解釋清楚什么是CMSGC太頻繁這個術語,相信不少小伙伴也是比較關心的,
如果你聽過垃圾搜集器中有一款名為CMS垃圾搜集器,那就好理解了,所謂的CMSGC太頻繁意思是說CMS垃圾搜集器在當下時間視窗垃圾收集的動作頻次太快(平時老半天才回收一次或幾次垃圾物件,現在可能一分鐘就需要回收多次),大致就是這個意思,

所以說CMS垃圾收集器是一款作用于老年代區域的垃圾收集器,
關于CMS+ParNew垃圾搜集器的配置說明:大家如果在VM啟動配置引數中做如下配置:-XX:+UseConcMarkSweepGC.該配置項首先是激活CMS收集器(作用于老年代),之后-XX:UseParNewGC會自動開啟,意味著年輕代將使用多執行緒并行垃圾收集器parNew進行回收,
原因分析
-
新生代因為垃圾回收之后,因為存活物件太多,導致Survivor空間放不下,部分物件會進入老年代 -
大物件直接進入老年代
這里的大物件是指那些需要大量連續空間的JAVA物件,比如那種很長的字串或陣列物件,
-
長期存活的物件將進入老年代
物件在Eden出生,并經過第一次YGC后任然存活,并且能被Survivor空間容納,將被移動到Survivor空間中,并且物件年齡設為1,物件在Survivor空間每熬過一次YGC,年齡就增加一歲,如果達到15(默認)歲,物件就會進入老年代,
-
動態物件年齡判斷
這點是對長期存活的物件進入老年代的補充, 其實不一定要必須滿足所謂的存活物件年齡達到15歲才能進入老年代,如果一次YGC后,盡管Survivor區域有空間能容納存活物件,但這批存活物件恰好存活的年齡相同,且加起來的大小總和大于Survivor空間的一半,這些物件照樣會進入老年代,
-
老年代可用的連續空間小于年輕代歷次YGC后升入老年代的物件總和的平均大小,說明YGC后升入老年代的物件大小很可能超過了老年底當期可用的記憶體空間;觸發cmsgc后再進行ygc
-
ygc之后有一批物件需要放入老年代,但老年代沒有足夠的空間存放了,需要觸發一次cmsgc
-
老年代的記憶體使用率超過92%,也要觸發OLD 程序(通過引數控制-xx:+CMSInitiatingOccupancyFraction)
排查程序
-
配置VM引數 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOGDIR}/ 虛擬機在OOM例外之后會自動生成一份dump檔案在本地 , -
執行jmap(Java記憶體映像工具)命令 jdk提供的命令列工具jmap能生成堆存盤快照,jmap -dump:format=b,file=heapdump.hprof {行程ID}

-
阿里開源性能診斷工具:Arthas

接下來作者用本次告警dump下來的堆檔案,用MAT工具給大家演示一下具體查找問題物件的全程序,
MAT是Memory Analyzer tool的縮寫,是一種快速,功能豐富的Java堆分析工具,能幫助你查找記憶體泄漏和減少記憶體消耗, 很多情況下,我們需要處理測驗提供的hprof檔案,分析記憶體相關問題,那么MAT也絕對是不二之選,Eclipse可以下載插件結合使用,也可以作為一個獨立分析工具使用, 下載地址:eclipse.org/mat/downloa,如果安裝程序中可能會碰到版本過低的問題,需要安裝一下高版本JDK 比如11,最后設定一下安裝路徑即可,
打開堆檔案
如果你已經成功安裝完MAT,進入首頁后就可以打開本地hprof檔案了,

打開檔案后,進入分析頁

-
Actions:
Histogram 列出每個類所對應的物件個數,以及所占用的記憶體大小;Dominator Tree 以占用總記憶體的百分比的方式來列舉出所有的實體物件,注意這個地方是直接列舉出的對應的物件而不是類,這個視圖是用來發現大記憶體物件的Top Consumers:按照類和包分組的方式展示出占用記憶體最大的一個物件Duplicate Classes:檢測由多個類加載器所加載的類資訊(用來查找重復的類)
-
Reports:
Leak Suspects:通過MAT自動分析當前記憶體泄露的主要原因
Top Components:Top組件,列出大于總堆1%的組件的報告 -
Step By Step:
Component Report:組件報告,分析屬于公共根包或類加載器的物件


關注上述兩個選項基本就能找到問題物件了,
解決方案
-
如果你的程式代碼書寫正常,純粹是真的應用流量太大,你部署的機器沒辦法抗住這波流量,這種情況發生CMSGC太頻繁概率就很大了,甚至最侄訓導致OOM例外,對這種情況也只能橫向擴充機器了,以均衡流量, -
如果你的機器足夠,線上流量也正常,但也發生了cmsgc太頻繁,甚至OOM例外,那大概率是你的程式代碼有問題,導致老年代區域聚集了大量垃圾物件,垃圾回收執行緒頻繁回收那些無用的垃圾物件,最終可能還達不到回收的理想效果,那么這個時候你不得不分析堆里面被大量占據的物件,看看是不是程式代碼問題導致老年代被堆滿, 像作者文章開始出的這個案例,作者經過上述步驟分析后,發現是程式代碼問題導致有大量物件進入老年代,(作者在應用中引入了一個java8的Nashorn組件,該組件的構建程序極其復雜,內部會創建很多個物件實體,因為作者的業務流量還是比較大的,每秒2000+QPS),機器也是夠的大概10臺(每臺4C8G),分析發現記憶體中大量充斥著Nashorn相關代碼,經過深入分析,其實這個Nashorn實體全域單例就可以了,不需要每次方法執行都構建一個實體,因為構建程序復雜且多物件,流量一高勢必最終導致應用發生記憶體溢位等例外,
總結
最后我也總結了應該如何避免發生GC太頻繁甚至OOM這類例外,如果程式代碼一切正常,純粹是瞬時流量太高才導致的GC動作加快,可以考慮臨時增加服務器實體,分攤流量,不過很多問題可能都是程式員代碼書寫不正確才導致的,這個時候你應該首先找出問題物件,然后找出頻繁創建物件的代碼塊,
本文完!
寫到最后
作為996的程式員,寫這篇文章基本都是利用作業日下班時間和周六周日雙休的時間才最終成稿,比較不易, 如果你看了文章之后但凡對你有所幫助或啟發,真誠懇請幫忙關注一下作者,點贊、在看此文,你的肯定與贊美是我未來創作最強大的動力,我也將繼續前行,創作出更加優秀好的作品回饋給大家,在此先謝謝大家了!關注我
如果這篇文章你看了對你有幫助或啟發,麻煩點贊、關注一下作者,你的肯定是作者創作源源不斷的動力,
公眾號

里面不僅匯集了硬核的干貨技術、還匯集了像左耳朵耗子、張朝陽總結的高效學習方法論、職場升遷竅門、軟技能,希望能輔助你達到你想夢想之地!
公眾號內回復關鍵字“電子書”下載pdf格式的電子書籍(JAVAEE、Spring、JVM、并發編程、Mysql、Linux、kafka、分布式等)、“開發手冊”獲取阿里開發手冊2本、"面試"獲取面試PDF資料,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/540718.html
標籤:其他
