Java GC記錄
近來、項目沒有特別忙碌的時候,抽空看了下生產環境的項目運行狀況,我們的項目一直運行速度不是很快,偶爾會出現卡頓的現象,這點給人的體驗感覺也就不那么好了。先拋個測試環境截圖(生產環境不方便,單參數設置類似):
由上圖可以看出,系統分配的堆內存4G,非堆內存最大2G,理論上、這個數值對於一般的項目絕對是夠用的了;然鵝、並非如此,事實情況確是:
對於系統GC的記錄統計為:
到這里、似乎還是看不出為何會產生這么多次的Full GC(大神應該已經察覺出上面的問題),再來看一張圖:
似乎問題在這里已經浮現了,雖然我們給Java的堆內存、非堆內存都分配了不少空間,但真正使用的到的空間,以及它們之間如何分配,卻沒有限定。如上圖存在的問題:
- 整個堆內存年輕代與老年代分別各占1.25G和2.67G,比例大約1:2.
- 永生帶使用內存峰值也就只有380M
- 整個堆內存與非堆內存總共分配4.52G,實際使用峰值時也只有3.87G
現在、再分析下造成頻繁Full GC的原因,了解過JVM GC知識的基本都清楚當在年輕代new一個對象時,如果年輕代沒有足夠的內存會進行一次Minor GC,當Minor GC之后仍無法提供足夠的內存時,年輕代會將數據移向老年代,若老年代內存充足,則直接移向老年代,若老年代內存不足,就會進行一次Full GC,當Full GC結束后,老年代依舊無法提供足夠的內存空間時,將拋出OutOfMemeryError錯誤;這里顯然不是拋出了異常,而是頻繁的進行Full GC。還有一種情況是非堆區內存不足時也會觸發Full GC,然而,上面的數據顯示非堆區內存充足,這里顯然不是頻繁Full GC的原因。所以、可以肯定這里的Full GC大多是老年代內存不足造成的。
看到上面的堆內存分布,我們知道老年代的內存占用峰值2.67G,依舊造成了頻繁的Full GC,分析具體原因如下:
- 年輕代內存占比偏大,導致每次轉移的Survivor區數據到老年代過大,致使老年代觸發Full GC,即使老年代可能還沒滿。
- 系統new的大對象較多,在老年代對象存活時間久,致使剩余的內存空間不足
- 由於業務需求的原因,事實就是堆內存分配不足
看到上面的原因分析,解決辦法如下:
- 修改年輕代與老年代堆內存使用比例(如1:3,1:4),使得Minor GC次數增多,從而減少Full GC次數。
- 優化業務代碼,提高對象的復用,使得大對象的創建數目減少。
- 增加堆內存的分配
總之、具體問題具體分析,希望可以解決問題。