一次频繁Full GC的排查过程,根源居然是它...
轉載自? ?一次頻繁Full GC的排查過程,根源居然是它...
業務部門的一個同事遇到個奇怪的 Full GC 問題,有個服務遷移到新的應用后,一直頻繁 Full GC。新應用機器的配置是 4c 8g,老應用是 4c 4g,老應用 GC 都很正常,并且代碼沒有變更,所以比較奇怪。
現象
問題的現象是,從監控圖上看一直有大量的 Full GC
?
排查
遇到這個問題,一般都是先看看各個區的內存占用情況:
?
從監控圖上看 Old Gen、Young Gen、Perm Gen,沒什么問題,不會觸發 Full GC,當然這里看各個 Gen 是否會觸發 Full GC 需要結合 JVM 參數配置來看。
順便也看了下 GC 日志,一直狂暴 CMS GC 日志,而且可以看到老年代使用空間也不大,細心可以發現,大量的 CMS GC 中夾雜著 Young、Perm 區的回收,所以其實是 Full GC。GC 日志如下:
?
老應用的 JVM 參數配置
?
新應用的 JVM 參數配置
?
通過上面的觀察,再根據一般觸發 CMS GC 幾個可能性:
-  
Old Gen 使用達到一定的比率,默認為92%,這里看 CMSInitiatingOccupancyFraction=80%,而實際才使用 2%(看監控圖表)不到,所以排除這種情況。
 -  
配置了 CMSClassUnloadingEnabled,且 Perm Gen 的使用達到一定的比率默認為 92%,這里看 CMSInitiatingPermOccupancyFraction=80%,而實際才使用 30%(看監控圖表)不到,所以排除這種情況。
 -  
配置了 ExplictGCInvokesConcurrent 且未配置 DisableExplicitGC 的情況下顯示調用了 System.gc()。
 -  
Hotspot 自己根據估計決定是否要觸法,如 CMS 悲觀策略,這類可以通過 GC 日志分析。
 
大致判斷很可能是 System.gc() 導致的問題,但是怎么定位調用 System.gc() 的代碼呢? 當時就想如果是 System.gc() 引起的頻繁 Full GC,jstack 線程堆棧應該能看到一些信息,果不其然,確實通過線程堆棧找到了。
jstack 作用非常大,很多問題都能從這里發現,而且比較輕量,對應用基本無影響。某次的 jstack 信息只代表那個時刻的線程堆棧,有時只看一個 jstack 信息可能看不出什么問題,一般可以多 jstack 幾次,然后對比去看,基本就能發現一些問題。 當然該問題,也可能不是頻繁的 Full GC,可能通過 jstack 定位不到問題,可以 jstat -gccause pid 1000,來查看 gc 原因。
很明顯,是由于 jxl 這個包中的 close 方法顯示調用了 System.gc() 導致的問題。
跟了下代碼,自然確實存在這段代碼,不過有個設置開關,可以 disable 這個功能,所以在使用的時候可以設置 setGCDisabled(true),關閉觸發 System.gc()。
但是為什么老應用沒有問題呢,主要是因為它 -XX:+DisableExplicitGC,屏蔽了 System.gc() 動作,新應用的 JVM 沒有這個配置。
可能大家還有個疑問,都知道 System.gc() 會觸發 Full GC,那為什么一直進行 CMS GC(通過GC日志)呢? 主要是因為這個參數 -XX:+ExplicitGCInvokesConcurrent,打開此參數后,會做并行 Full GC,只有配置 -XX:+UseConcMarkSweepGC 這個參數,該參數才會生效。因此,System.gc() 時 Old 區會進行 CMS GC,可提高 Full GC 效率。
總結
盡量減少顯示使用 System.gc() 來觸發 Full GC,這會導致頻繁 Full GC,非常影響應用性能。
總結
以上是生活随笔為你收集整理的一次频繁Full GC的排查过程,根源居然是它...的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 有了网关路由器密码如何修改家里路由器怎么
 - 下一篇: 如何判断是不是公网ip怎么看自己是不是公