一次線上機器load負載過高報警問題排查及其後續處理
問題來源:從3.14號開始陸續收到線上一臺機器的負載過高報警
問題排查 :
於是對gc、堆記憶體、load負載、cpu使用情況等進行了統計分析。
gc時間圖示
堆記憶體使用情況:
load負載
cpu使用率
通過以上對gc的統計,堆記憶體的使用情況,負載和cpu使用率的統計,我們不難發現其實在發生報警的時間範圍內,不論是gc、堆記憶體、load負載還是cpu使用情況都是有一個上升區,然後到達峰值後下降並以此迴圈,所不同的是在3.14號資料量比較大最終暴露出了問題。從ygc時間曲線圖看出,ygc非常不正常,在一個波動週期內ygc每次的時間會越來越長,換句話說ygc回收堆記憶體的速度遠遠沒有新物件例項的速度快,也可能是堆記憶體中例項化的一些物件通過ygc並不能回收掉。ygc的處理時間越久對cpu的消耗越大,因為我們觀察到,網絡卡流量、磁碟io並沒有瓶頸。隨後我們對線上執行的專案進行了dump堆疊資訊,並是用MAT分析工具進行分析,發現記憶體中存在一個很可疑物件CompositeClassLoader,而這個物件的引入是在xml和Object物件進行轉換的時候,引入的Xstream開原始碼引入的。檢視程式碼發現,我們每次進行一條資料的換的時候都是通過new Xstream()例項的方式,而且這部分的程式碼基本上都是定時任務查詢出一批批的訂單,然後由物件轉xml傳送到各外部介面,並接收外部介面的xml資訊解析成Object物件,於是乎也就是說我們查到的所有對單都會在newXstream例項的時候新建一個Xstream物件,而這個物件又有什麼影響呢,檢視原始碼並且搜尋文件發現:引用Xstream官網中的話The XStream instance is thread-safe. That is, once the XStream instance has been created and configured, it may be shared across multiple threads allowing objects to be serialized/deserialized concurrently也就是說,XStream是執行緒安全的,我們只需要為每種型別例項化一個物件即可。而我們在程式中卻每次都new了一個Xstream物件。而new XStream物件時,基於JVM的類委託載入機制,該構造方法會根據類名載入類的操作,轉變為new 大量new自定義的ClassLoader來載入,會產生大量ClassLoader物件以及parallelLockMap(ConcurrentHashMap)物件,導致產生大量的Segment分段鎖物件,大大增加了GC Roots的數量,導致YGC中的標記時間變長。從而釋了這個時間段內 YGC時間越來越長、負載越來越高的問題。
該問題解決方案
該問題其實是外部開原始碼使用方式不正確,應該為每種型別例項化一個Xstream物件即可。可以單例處理,也可以靜態變數初始化,當然也可以使用池化技術。
後記
在使用開原始碼的時候,一定要去官方網站,後者其它一些技術網站上了解該原始碼的正確使用方式,避免給程式帶來災難。
以上效能問題算是解決了,但是僅僅只需要例項化一個Xstream例項,後續再只需要這個Xstream例項就可以嗎?其實這裡改造上線後也遇到了一個坑。請見我的下一篇文章,希望對有遇到過此問題的同學有所幫助。