多圖預警!java
從JVisualVM的監控視圖中,咱們能夠直觀的看出每隔一分鐘都會出現線程數飆升、類加載數階梯式增加的狀況。web
隨着類加載數的增加,Metaspace空間逐步從60M增加到100M,出現內存溢出,致使jvm頻繁觸發full GC,消耗大量CPU資源。緩存
Task類 run方法代碼:tomcat
紅框部分爲新增代碼,具體實現以下:安全
主要邏輯是與底層組件通訊查詢運行狀態,而後根據結果更新狀態。直接排除DAO操做的嫌疑,抽取與通訊部分,整理成單獨的測試代碼:服務器
步驟: 1. 設置jvm參數 : -verbose:class 打印類加載信息併發
2. 清理控制檯日誌,調試代碼。jvm
調試過程當中,我發現每次循環都會有新的類被加載:工具
而這些類都是在下面這行代碼運行以後加載的。測試
結合類加載信息以及sendRequest方法的實現,基本確認問題是由JaxbUtil處理xml、JavaBean的相互轉換引發。
繼續調試分析,發現JAXBContext對象初始化時會動態加載class,而JaxbUtil每次調用都會從新建立一個JAXBContext。
問題根因既已找到,解決思路天然清晰明確。
考慮到jdk中已有JAXB工具類提供xml和javaBean的互轉,借鑑源碼發現JAXB使用弱引用Cache對象來緩存JAXBContext。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
結合應用的實際場景,上面的實現避免了短期頻繁建立JAXBContext。可是弱引用Cache在無引用的狀況下會很快被GC回收,因此每次定時任務都會從新生成context;而且Cache對象只能存儲一個context,在定時任務的運行過程當中可能因爲其餘接口通訊致使context切換。綜上,JAXB的實現也沒法知足當前應用的須要。
沒有現成的解決方案,只好本身寫一個。
由建立JAXBContext引發問題,那就延長對象的生命週期,減小新建對象。對於相同的Class,可使用同一個context對象與xml互相轉換。因爲vag的接口個數有限, 其xml報文格式並很少,所以,維護一個static Map<Class<?>, JAXBContext>來存儲context對象佔用的內存並很少。考慮到與vag通訊屬於併發執行,使用ConcurrentHashMap實現保證併發安全。
最終代碼以下:
將以前的測試代碼模擬定時任務略微修改,每隔10s執行一次,重複50次。
開啓JVisualVM監視視圖,從圖中能夠明確的看出類裝載數在第一次循環時就已接近最大值,後續過程當中只加載了極少數量的class,證實這種方案確實可行。
使用修改後的代碼運行整個項目,16小時後的監視圖像顯示:類加載數保持穩定,MetaSpace大小几乎無變化。
另外,前段時間還使用-verbose:class 參數排查出Apache CXF生成的webservice客戶端重複初始化引發的OOM問題的緣由。客戶端初始化的過程當中也會根據wsdl文件動態生成class並加載,所以,使用CXF客戶端代碼時,應儘可能使用單例模式。