fgc java,频繁FGC的真凶原来是它
頻繁FGC的真兇原來是它
上周排查了一個線上問題,主要現象是CPU占用過高,jvm old區占用過高,同時頻繁fgc,我簡單排查了下就草草收場了,但是過后我對這個問題又進行了復查,發現問題沒有那么簡單,下面跟著我一起分析一下到底是怎么回事?
復查過程
復查原因
事后再看dump文件注意到最大的對象是一個ArrayList,里面幾乎都是ElasticSearchStatusException對象
可是發生這個異常的操作上次已經被我定位到了,數據漏斗只有產品、運營等內部人員使用,通過使用頻率推測,不應該有那么多對象。我猜想是不是代碼中存在死循環,但沒有找到。沒辦法只能在測試環境進行場景復現了。
復原現場
通過上次排查到是es查詢了不存在的索引導致異常,所以就把查詢es的索引寫死一個不存在的,最初嘗試寫個單測,但一直不能復現問題,所以只好部署到測試環境,在本地通過遠程debug 調試程序
遠程debug到斷點時,發現源碼對不上
然后發現有可選擇的源碼,這里是關鍵
從org.apache.commons.lang:2.5jar包切換到springsource.org.apache.commons.lang:2.1.0包后,竟然能夠和測試環境對得上,可是代碼中明明引用的commons.lang:2.5的包,這里說明在項目中類加載的時候,ExceptionUtils這個class文件并不是從commons.lang中加載的,而是從springsource包中加載的 關于類文件加載的問題我們先放到后面,先找代碼的問題
看到這里,眼尖的朋友應該已經發現了bug的所在,那么恭喜你。如果沒發現的朋友,不要著急,跟我一步一步來。我們繼續debug跟進代碼的 getCause方法,可以看到通過遍歷異常名字的數組驗證是否在拋出的異常中存在
這些異常方法中的getRootCause方法,存在ElasticSearchStatusException的父類ElasticsearchException中
我們看下這個方法,主要找最根本的異常原因,有則返回,沒有就返回當前的異常
繼續跟代碼,cause不為null,返回這個異常
bug代碼定位
這個getThrowables方法,里面有個while循環,判斷條件只進行了非空判斷,不為null就添加到list中,注意觀察我截圖的時刻,list的大小 8萬多,其實遠遠不止會看開頭dump文件的大對象,是一個ArrayList,里面有大量的ElasticSearchStatusException對象
其實到這里已經定位到了FGC的真兇,判斷條件沒有排除返回的異常是已經添加到list中的異常,所以會一直循環添加,造成堆內存占用滿了,FGC回收不掉這些對象,因為ArrayList一直持有他們的引用
正確代碼應該如下面這樣,所以開源工具庫也是會有bug的,用的時候多加注意
public static Throwable[] getThrowables(Throwable throwable) {
List list = new ArrayList();
// 這里的判斷條件應該加上 list.contains(throwable) == false
while (throwable != null && list.contains(throwable) == false) {
list.add(throwable);
throwable = ExceptionUtils.getCause(throwable);
}
return (Throwable[]) list.toArray(new Throwable[list.size()]);
}
本來我們就沒想用springsource的方法,只是類加載的時候加載錯了,那看下commons.lang包下的方法是否正確呢?可以看到這個包的方法是正確的,考慮到了這個問題
springsource的commons.lang包在2.2版本已經修復了這個問題 jar包最好引用最新的
class文件加載問題
上面我們留了一個jvm加載class文件的問題,我們知道jvm加載class的時候,如果存在包名和類名完全一樣,先加載一個后,另外的就不會再被加載了。
經查看兩個ExceptionUtils確實包名類名完全一致
其實通過前面的debug和代碼分析,已經能確定項目加載的ExceptionUtils.class文件來自springsource包,但還是想通過一定手段驗證一下。
其實jvm提供類類似的功能參數,修改項目啟動腳本,添加jvm參數 -XX:+TraceClassLoading 然后重啟項目并繼讓異常重現【這里要讓觸發異常重現,是因為是運行時異常】,并查看日志,查看ExceptionUtils.class的加載信息
可以看到確實和我們推測的一樣
其實這里還可以深入研究jvm的類加載機制,類加載器加載順序,雙親委派模型等
如何解決
通過 mvn dependency:tree 查看jar包依賴情況,排除掉不用的jar包
結尾
到這里這個問題的排查應該告一段落了,從排查過程中學到了不少,場景復現,梳理思路,用文章分享出來雖說碼字不易很耗時,但這是對自己的一種總結,里面還是有很多樂趣與收獲的。
后續會繼續分享一些和廣告系統相關的文章,敬請期待!
歡迎關注公眾號 【每天曬白牙】,獲取最新文章,我們一起交流,共同進步!
總結
以上是生活随笔為你收集整理的fgc java,频繁FGC的真凶原来是它的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: phpmyadmin出现405错误怎么办
- 下一篇: Asp.Net_文件操作基类