俗話說技多不壓身,當年苦讀《深入理解JVM》還專門整理了筆記,現在就用上了~ 筆記 http://www.cnblogs.com/syjkfind/p/3901774.html 【癥狀】 用戶操作數據導出時總會發生卡頓,後臺占記憶體的定時任務發生時也會。JVM參數就不貼了,比較普通且相對合理。 【思路 ...
俗話說技多不壓身,當年苦讀《深入理解JVM》還專門整理了筆記,現在就用上了~
筆記 http://www.cnblogs.com/syjkfind/p/3901774.html
【癥狀】 用戶操作數據導出時總會發生卡頓,後臺占記憶體的定時任務發生時也會。JVM參數就不貼了,比較普通且相對合理。
【思路】
查gc日誌是發生了full gc,tomcat日誌零零散散有很多exception。
另外憑著對代碼的瞭解,觸發時同時刻的日誌顯示正在執行較大數據量的查詢而且裝載進JVM,方法調用和臨時變數也很多。
【分析】
1.minor gc很頻繁,但時間短所以問題不大,觸發原因基本都是申請空間失敗。
2.偶爾有System.gc(),時間大概1分鐘。代碼中沒有顯式調用,基本確定是監控程式RMI訪問觸發的。可以加參數禁用 -XX:+DisableExplicitGC 。
3.時常有promotion failed,即在minor gc時年輕代的存活區空間不足而進入老年代,老年代又空間不足而觸發full gc。時間大概3分鐘。解決思路一種是增大存活區,一種則相反是去掉存活區增大老年代。相關參數一般有:
-XX:SurvivorRatio=32 存活區除以伊甸區的比率,存活區有from和to兩個,所以這裡的意思是單個存活區占年輕代的1/34;
-XX:OldSize=60M 老年代大小;
-XX:MaxTenuringThreshold=15 即多少次minor gc後存活的年輕代對象會晉升老年代。
4.經常有concurrent mode failure,即CMS執行過程中老年代空間不足,這時會變成Serial Old收集器導致更長時間的停頓。時間大概5分鐘。其中引發這一問題的情況可能是浮動垃圾太多、可能是CMS收集器本身也占用堆空間、也可能是老年代太多碎片,但都是CMS收集器的特性導致的。相關配置一般有:
-XX:CMSInitiatingOccupancyFraction=80 即老年代滿80%時觸發CMS(full gc),調高則full gc相對減少,調低則full gc處理得比較快;
-XX:UseCMSCompactAtFullCollection 或 -XX:CMSFullGCsBeforeCompaction=5 即full gc前或後做碎片整理。
5.另有個有趣的文章,過多過長的exception會導致promotion failed
http://www.th7.cn/Program/java/201511/685711.shtml
可能挺符合我們的場景的,沒有運維許可權,下次可以考慮找運維dump個分析一下對象。
以前為了追蹤錯誤關閉了“快拋” -XX:-OmitStackTraceInFastThrow 即反覆出現的exception不會用一個靜態的沒堆棧信息的實例去代替,這個要權衡一下了。
【解決】
創建很多對象占了年輕代?大對象導致占滿老年代?浮動垃圾(CMS時繼續在產生臨時對象)或老年代碎片?
調優多少是可以改善的,但似乎代碼本身更有問題,恐怕分配再多的記憶體都不夠吃。比如沒有應用較完善的一級緩存二級緩存,比如經常粗暴地把上萬條記錄載入進來,還沒做分頁,還有藏的比較深的做了切麵觸發非同步服務把非同步服務隊列占滿了。初期享受了框架的便利而考慮設計的少,而現在則要開始還技術債務了~
所幸的是在有限的時間里,我們有簡單粗暴有效的方法緩解這些問題:加伺服器,把大任務剝離出來~
而中長期的考慮來看,在不改變現有框架的前提下,整改exception、整改業務代碼做分頁、封裝資料庫分頁查詢框架、改造資料庫層二級緩存(集群範圍內緩存)會是比較有效的措施。
更長遠的肯定就是回歸Spring系主流框架,再者進軍服務化了。。。但那個幾乎要把業務代碼重新寫一遍,是後話了。
回歸主題如果是調優來解決問題,最好是dump了做更深入的分析。主觀上看來啟用快拋、gc後碎片整理、增大存活區應該是比較有針對性的調整,而CMSInitiatingOccupancyFraction取值和CMS各階段細緻的配置則需要更量化的分析。