問題描述:
早上去公司上班,忽然就郵件一直報警,接口報異常,而後去查服務器的運行狀況,發現java的cpu爆了。接着就開始排查問題java
問題解決過程:
1.先服務器(centos7)上,使用了top和uptime命令,發現時java的cpu爆了,超過100%了,致使後續的服務沒法正常提供; 2.調整了負載均衡,下掉了有問題的那幾臺機器;
3.使用jps找到了運行着的tomcat的pid,這裏假設爲10086;
4.使用jstat -gcutil 10086 500 10 (意思是對pid爲10086的線程,每500ms顯示各分代的內存使用狀況), 這裏給一下部分jvm的參數設置,以下:算法
能夠看到對新生代使用的是ParNew收集器,對老年代使用的是CMS收集器,CMSInitiatingOccupancyFraction=80說明當使用率超過80%的時候觸發垃圾回收。而後發現,線上老年代一直超過80%的使用率,幾乎一秒不到就進行了一次FGC,這麼頻繁的FGC致使了服務沒法正常運行;
5.使用jmap -histo 查看了是哪些對象的數量最多,若是參數是-histo:live的話,會在進行一次FGC後,顯示當前的使用數量最多的實例。查看後發現有大量的ConcurrentHashMap的Node節點實例,因而去代碼中搜索使用到ConcurrentHashMap的地方,有好幾處使用比較頻繁的地方,看了代碼後分析,多是圖片上傳,下載的問題,但也不能判定。
6.使用jmap -dump:format=b,file=/usr/local/tomcat/dump1將內存的狀況給拉下來。以下:
文件生成後,將dump1放入到eclipse的mat中進行分析。直接顯示了一個疑似內存泄漏的問題。而後分析dump文件給出的信息,發現一個叫IdleConnectionReaper的類。dump文件裏面說的內存泄漏的大概的意思就是說,IdleConnectionReaper這個類裏面的ArrayList存放的東西太多了,爆掉了。以下:
從oss的jar包裏面找到這個類之後,簡單的看一下這個類的構成,以下:
經過查找一些官方的資料和源代碼的閱讀,發現這個是一個oss的守護線程,用來檢測上傳或者下載的工做線程,每60秒就會去檢查一下空閒的工做線程,而且將它們回收。而後它內部有一個靜態的ArrayList,裏面保存的是ossClient的連接,默認是1024個。
7.因此大概緣由找到了,就是ossClient的連接太多了,扛不住了,因此一直在進行FGC,致使服務不可用了,最後找到相關的代碼,發現有個小方法裏面在每次上傳或者下載的時候,都會去建立一個ossClient。修改了代碼將ossClient調用的地方改爲了單例。修改完線上跑了一段日子,後來也沒有出現過這樣的問題。
總結:
1.大量的請求,調用的地方要注意是否會致使內存的大量消耗,儘量使用池化技術,單例等,減小建立,銷燬的系統開銷;
2.CMS 的幾個缺點,能夠參考《深刻java虛擬機》,對CPU佔用會比較高,沒法處理浮動垃圾,還有就是CMS使用的是標記-清除算法,會致使大量的空間碎片,碎片過多的話,致使分配大對象很困難,因此不得不進行FGC,也多是這個緣由致使了本文說的一直FGC的問題。解決方式: -XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啓對年老代的壓縮(會整理內存碎片,默認是開啓的)。 -XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的狀況下,這裏設置多少次Full GC後,對年老代進行壓縮,會使得FGC的時間變長,可是能夠提高內存空間的使用率,讓大對象能夠更容易分配,而不須要屢次FGC。centos