[JVM面試]Full GC 到底是如何產生的?如何解決?

點擊上方“Coder編程”,選擇“置頂公衆號”

技術文章第一時間送達!

Full GC 到底是如何產生的?如何解決?
                                

前言

Full GC相對於Minor GC來說,停止用戶線程的STW(stop the world)時間過長,至少慢10倍以上,所以要儘量避免,首先說一下Full GC可能產生的原因,接着給出排查方法以及解決策略。

Full GC產生原因

下圖爲與產生Full GC相關的內存區域,初生代、老年代、以及Metaspace區域。

內存區域

System.gc()方法的調用

在代碼中調用System.gc()方法會建議JVM進行Full GC,但是注意這只是建議,JVM執行不執行是另外一回事兒,不過在大多數情況下會增加Full GC的次數,導致系統性能下降,一般建議不要手動進行此方法的調用,可以通過

-XX:+ DisableExplicitGC來禁止RMI調用System.gc。

老年代(Tenured Gen)空間不足

Survivor區域的對象滿足晉升到老年代的條件時,晉升進入老年代的對象大小大於老年代的可用內存,這個時候會觸發Full GC

Metaspace區內存達到閾值

從JDK8開始,永久代(PermGen)的概念被廢棄掉了,取而代之的是一個稱爲Metaspace的存儲空間。Metaspace使用的是本地內存,而不是堆內存,也就是說在默認情況下Metaspace的大小隻與本地內存大小有關。

-XX:MetaspaceSize=21810376B(約爲20.8MB)

超過這個值就會引發Full GC,這個值不是固定的,是會隨着JVM的運行進行動態調整的,與此相關的參數還有多個,詳細情況請參考這篇文章
http://www.importnew.com/22886.html ( jdk8 Metaspace 調優)

統計得到的Minor GC晉升到舊生代的平均大小大於老年代的剩餘空間

Survivor區域對象晉升到老年代有兩種情況:

  • 一種是給每個對象定義一個對象計數器,如果對象在Eden區域出生,並且經過了第一次GC,那麼就將他的年齡設置爲1,在Survivor區域的對象每熬過一次GC,年齡計數器加一,等到到達默認值15時,就會被移動到老年代中,默認值可以通過

-XX:MaxTenuringThreshold來設置。

  • 另外一種情況是如果JVM發現Survivor區域中的相同年齡的對象佔到所有對象的一半以上時,就會將大於這個年齡的對象移動到老年代,在這批對象在統計後發現可以晉升到老年代,但是發現老年代沒有足夠的空間來放置這些對象,這就會引起Full GC。

堆中產生大對象超過閾值

這個參數可以通過

-XX:PretenureSizeThreshold

進行設定,大對象或者長期存活的對象進入老年代,典型的大對象就是很長的字符串或者數組,它們在被創建後會直接進入老年代,雖然可能新生代中的Eden區域可以放置這個對象,在要放置的時候JVM如果發現老年代的空間不足時,會觸發GC。

老年代連續空間不足

JVM如果判斷老年代沒有做足夠的連續空間來放置大對象,那麼就會引起Full GC,例如老年代可用空間大小爲200K,但不是連續的,連續內存只要100K,而晉升到老年代的對象大小爲120K,由於120>100的連續空間,所以就會觸發Full GC。

CMS GC時出現promotion failed和concurrent mode failure

這個原因引發的Full GC可以參考這篇文章,下面也摘抄自這篇文章:
http://www.importnew.com/22886.html (JVM 調優 —— GC 長時間停頓問題及解決方法)

提升失敗(promotion failed),在 Minor GC 過程中,Survivor Unused 可能不足以容納 Eden 和另一個 Survivor 中的存活對象, 那麼多餘的將被移到老年代, 稱爲過早提升(Premature Promotion)。這會導致老年代中短期存活對象的增長, 可能會引發嚴重的性能問題。 再進一步, 如果老年代滿了, Minor GC 後會進行 Full GC, 這將導致遍歷整個堆, 稱爲提升失敗(Promotion Failure)。
在 CMS 啓動過程中,新生代提升速度過快,老年代收集速度趕不上新生代提升速度。在 CMS 啓動過程中,老年代碎片化嚴重,無法容納新生代提升上來的大對象,這是因爲CMS採用標記清理,會產生連續空間不足的情況,這也是CMS的缺點

總結

可以發現其實堆內存的Full GC一般都是兩個原因引起的,要麼是老年代內存過小,要麼是老年代連續內存過小。無非是這兩點,而元數據區Metaspace引發的Full GC可能是閾值引起的,詳細原因還是建議參考其他文章,我就不誤人子弟了。

檢測JVM堆的情況

可以使用JDK的bin目錄下的jvisualvm.exe工具來進行實時監測,這個是圖形化界面,最爲直觀,這是一個強大的工具。
採用jps找到進行id,然後使用jstat -gc pid來實時進行檢測。
運行程序前設置

-XX:+PrintGCDetails,-XX:+PrintGCDateStamps參數打印GC的詳細信息進行分析。

監測圖
測試
命令

解決策略

如果是發現由於老年代內存過小頻繁引起的Full GC,那麼可以適當增加老年代的內存大小,如果是發現是由於老年代沒有連續空間來讓初生代的對象晉升,如果是採用CMS,那麼可以設置進行 n 次 CMS後進行一次壓縮式Full GC,參數如下:

-XX:+UseCMSCompactAtFullCollection:允許在 Full GC 時,啓用壓縮式 GC

-XX:CMSFullGCBeforeCompaction=n     在進行 n 次,CMS 後,進行一次壓縮的 Full GC,用以減少 CMS 產生的碎片。

除此之外,儘量少創建大對象,不要在代碼裏調用System.gc(),什麼時候進行Full GC這種事情還是交給JVM來做。在讀取文件後記得釋放資源,不要讓JVM無法回收垃圾,造成內存泄漏。

作者:Hollake 原文:https://blog.csdn.net/Hollake/article/details/90484027

參考文獻

  • https://blog.csdn.net/chenleixing/article/details/46706039

  • https://blog.csdn.net/YHYR_YCY/article/details/52566105

  • https://blog.csdn.net/bolg_hero/article/details/78189621

  • http://www.importnew.com/22886.html

推薦

Redis面試題集錦(精選)

Spring面試題集錦(精選)

SpringMVC面試題集錦(精選)

HR面試都會問什麼問題?(上)

HR面試都會問什麼問題(下)

文末

歡迎關注個人微信公衆號:Coder編程
歡迎關注Coder編程公衆號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~
新建了一個qq羣:315211365,歡迎大家進羣交流一起學習。謝謝了!也可以介紹給身邊有需要的朋友。

文章收錄至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
歡迎關注並star~

微信公衆號

                    我知道你 “在看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章