導讀
本文是線上問題處理案例系列之一,旨在通過真實案例向讀者介紹發現問題、定位問題、解決問題的方法,本文講述了從垃圾回收耗時過長的表象,逐步定位到資料庫連接池保活問題的全程序,并對其中用到的一些知識點進行了總結,
一、問題描述
大促期間,某介面超時次數增多,經排查直接原因是GC耗時過長,查看監控FullGC達500ms以上,介面超時時間與FullGC發生時間吻合,
圖1 FullGC耗時監控
二、應用基本情況
- 容器:8C12G;
- JVM配置:-XX:+UseConcMarkSweepGC -Xms6144m -Xmx6144m -Xmn2048m -XX:ParallelGCThreads=8 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ParallelRefProcEnabled;
- 資料庫型別:MySQL;
- 資料庫連接池:DBCP;
三、排查程序
1、 GC耗時過長,說明記憶體中垃圾物件很多,
2、 首先懷疑是否有記憶體泄漏,觀察FullGC后堆記憶體回收情況,尚屬正常,暫時排除記憶體泄漏原因,
圖2 發生FullGC后堆記憶體回收監控
3、 推斷FullGC耗時過長是否因為老年代有大量死亡物件,遂匯出FullGC前后堆記憶體dump,通過比對“保留大小”,發現FullGC后大量資料庫相關物件被回收,
圖3 堆記憶體物件分析
4、 資料庫連接正常應該不會頻繁創建和斷開,進入老年代后,正常不應該被回收,通過堆dump內容OQL分析每個資料庫連接數量,發現很多庫連接數都大于“maxActive”數量,可以肯定有很多失效連接,
5、 初步判斷直接原因是很多失效資料庫連接進入老年代,導致FullGC耗時過長,
6、 懷疑連接池驗證周期過長,導致資料庫因空閑過長關閉連接,將連接池引數“
timeBetweenEvictionRunsMillis”由1分鐘調整到10秒,問題依舊,
7、 閱讀DBCP原始碼,發現是通過
org.apache.commons.pool.impl.GenericObjectPool.Evictor定時任務,按照timeBetweenEvictionRunsMillis配置的周期定時驅逐失效連接,驅逐條件:若連接空閑時間大于“minEvictableIdleTimeMillis”,則會驅逐連接,等待垃圾回收,若開啟“testWhileIdle”則會執行“validationQuery”,進一步閱讀代碼,發現執行“validationQuery”后,連接空閑時間并不會重新計算,導致連接在業務低谷時很容易被淘汰,而資料庫連接會關聯大量物件,創建、回收成本昂貴,并且影響GC,
8、 反向思考,為何只有在大促期間才發生問題?
圖4 平時和大促時回收頻率對比
可以看到平時由于業務量小,GC不頻繁,過期連接沒有達到進入老年代閾值,在年輕代被回收,而大促時業務量大,GC頻繁,連接在進入老年代以后才過期,導致老年代FullGC時間過長,
9、 至此,基本可以肯定問題原因是資料庫連接池不具備“保活”能力,導致連接不斷淘汰和新建,在業務高峰時段,連接進入老年代然后失效,造成FullGC耗時過長,最終導致介面超時次數增多,
四、解決方案
方案1:改為G1回收器,對老年代回收是分塊進行,可以防止長時間停頓,另外默認MaxTenuringThreshold值是15,可以防止失效連接過早進入老年代;
方案2:
minEvictableIdleTimeMillis設定為0,使資料庫連接不會自動失效,進入老年代以后一直存活,避免在老年代失效回收;
五、問題總結
資料庫連接池并不具備通常理解的“保活”能力,資料庫連接在業務不活躍的應用中,會不斷淘汰和重連,而連接會通過虛參考方式(
com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference)攜帶大量物件,如果連接存活時間內YGC次數達到壽命閾值,則會進入老年代,老年代是使用“標記-清除”演算法,回收成本更高,進而造成FullGC耗時過長,
六、拓展知識點
1、 Druid連接池同樣存在不能“保活”問題,較新版本提供“KeepAlive”選項(未驗證);
2、 Druid連接池配置的“validationQuery”陳述句通常并不會被執行,MySqlValidConnectionChecker在檢查連接有效性時,會判斷驅動是否實作pingInternal方法,如果實作則會通過此方法驗證有效性,MySQL的JDBC驅動實作了該方法,因此“validationQuery”配置的陳述句通常不會執行;
圖5 連接有效性校驗代碼
3、 DBCP和Druid連接池默認都是FILO,如果業務不繁忙,會導致只有最前邊的連接被使用-歸還-使用,后邊連接基本都在無謂的驅逐、重建連接;
4、 虛參考對GC的影響:這些參考只有經過兩次GC才能被回收掉,如果進入老年代,則必須經過兩次FullGC才能釋放記憶體,本例中由于不斷有新的虛參考物件在老年代失效,導致FullGC后,記憶體水位仍然偏高,會加劇GC壓力,新版本JVM已對此做了優化,一次GC可以回收掉;
5、 類似的影響還有finalize方法;
6、 CMS回收器默認MaxTenuringThreshold為6,而ParallelGC和G1均默認15;
結語
本文對資料庫連接失效引起的GC問題進行了詳細分析,希望讀者通過本文對資料庫連接“保活”機制、GC問題基本分析方法有所收益,后續該系列文章會繼續推出其他案例分享,
作者:京東零售 王利輝
內容來源:京東云開發者社區
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/553145.html
標籤:其他
上一篇:看完這篇,DWS故障修復不再愁
下一篇:返回列表
