一次頻繁cms gc問題的排查

前言

前幾天收到線上某機器cms-initial-remark次數(shù)超過13次報警搬卒,這臺機器長時間沒有過新功能上線棕兼,為啥會出現(xiàn)頻繁cms gc告警呢梅掠,遂一起排查。

輸出啟動參數(shù)

登陸了問題發(fā)生的機器玉掸∪恤铮考慮到是gc問題,首先想到的是輸出虛擬機的參數(shù)司浪,使用ps或者jps找到j(luò)ava進程泊业,然后使用jinfo輸出啟動參數(shù),啟動參數(shù)如下:

 -Djava.util.logging.config.file=/home/admin/xxx/.default/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dcatalina.vendor=alibaba -Djava.security.egd=file:/dev/./urandom -Dlog4j.defaultInitOverride=true -Dorg.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE=true -Dorg.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true -Xms4g -Xmx4g -Xmn2g -XX:PermSize=96m -XX:MaxPermSize=256m -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:CMSMaxAbortablePrecleanTime=5000 -XX:CMSInitiatingOccupancyFraction=80 -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs -Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Djava.awt.headless=true -Dsun.net.client.defaultConnectTimeout=10000 -Dsun.net.client.defaultReadTimeout=30000 -XX:MaxDirectMemorySize=1g -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExplicitGCInvokesConcurrent -Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000 -XX:ParallelGCThreads=4 -DJM.LOG.PATH=/home/admin/logs -DJM.SNAPSHOT.PATH=/home/admin/snapshots -Dfile.encoding=GB18030 -Dhsf.publish.delayed=false -Dhsf.server.ip=11.183.92.169 -Dproject.name=xxx -Djava.endorsed.dirs=/opt/taobao/tomcat/endorsed -Dcatalina.logs=/home/admin/xxx/logs/catalina -Dcatalina.base=/home/admin/xxx/.default -Dcatalina.home=/opt/taobao/tomcat -Djava.io.tmpdir=/home/admin/xxx/.default/temp

同時確定了機器jdk的版本仍然是1.6

挑選出與gc有關(guān)的啟動參數(shù)分析一下:

  • -Xms 4g -Xmx4g啊易,最小最大堆內(nèi)存為4g吁伺,鎖定堆大小,不動態(tài)調(diào)整租谈。
  • -Xmn2g篮奄,年輕代占2g。
  • -XX:SurvivorRatio=10割去,表示兩個survivor : eden = 2 : 10,單個survivor占年輕代的1/12
  • -XX:PermSize=96m -XX:MaxPermSize=256m窟却,永久代初始值為96m,最大值256m
  • -XX:+UseConcMarkSweepGC 使用cms gc收集算法
  • -XX:+UseCMSCompactAtFullCollection 在發(fā)生‘full gc’時對老年大內(nèi)存空間進行整理壓縮
  • -XX:+CMSClassUnloadingEnabled 在cms gc時對永久代進行回收(卸載類或者卸載類加載器)
  • -XX:CMSMaxAbortablePrecleanTime=5000 AbortablePreclean是cms過程中的一個階段呻逆,用于平衡ygc 和remark階段(避免連續(xù)兩次stw)夸赫,設(shè)定該階段最長時間為5s
  • -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly 合起來用表示老年代used占capacity 80%的時候開啟cms collection cycle.
  • -XX:MaxDirectMemorySize=1g 最大直接內(nèi)存大小為1g
  • -XX:+ExplicitGCInvokesConcurrent,將顯示調(diào)用System.gc()轉(zhuǎn)化成一次cms gc(本應(yīng)發(fā)生full gc)咖城。
  • XX:ParallelGCThreads=4 并行收集的gc線程數(shù)為4,所以cms線程數(shù)ConcGCT
    hreads = (3 + ParallelGCThreads) / 1 = 1

GC日志

A機器

要定位gc問題茬腿,最核心的就是看gc日志定位問題。用工具dump了gc log分析宜雀,結(jié)果如下:

不看不知道切平,一看嚇一跳。

  • cms gc的cycle數(shù)量達到了驚人的6543次
  • 存在4次Mix GC州袒,并且由‘promotion failed’原因?qū)е?/li>
  • 存在一次perm區(qū)溢出的cms gc

存在的問題已經(jīng)比較清楚了

  • 頻繁發(fā)生cms gc
  • 存在一定程度的promotion failed(這種gc的傷害比較大揭绑,應(yīng)該盡量避免)
  • 存在意思perm溢出

那為啥會頻繁的開始cms collection cycle呢,先挑選幾條cms的日志

2017-10-12T22:35:56.602+0800: 777539.404: [GC [1 CMS-initial-mark: 950754K(2097152K)] 1939671K(4019584K), 0.7363400 secs] [Times: user=0.73 sys=0.01, real=0.74 secs]
2017-10-12T22:35:57.339+0800: 777540.141: [CMS-concurrent-mark-start]
2017-10-12T22:35:57.988+0800: 777540.791: [GC 777540.791: [ParNew: 1815272K->70343K(1922432K), 0.0484420 secs] 2766027K->1021205K(4019584K), 0.0486410 secs] [Times: user=0.15 sys=0.00, real=0.05 secs]
2017-10-12T22:35:59.137+0800: 777541.940: [CMS-concurrent-mark: 1.748/1.799 secs] [Times: user=4.08 sys=0.08, real=1.80 secs]
2017-10-12T22:35:59.137+0800: 777541.940: [CMS-concurrent-preclean-start]
2017-10-12T22:35:59.149+0800: 777541.952: [CMS-concurrent-preclean: 0.011/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2017-10-12T22:35:59.149+0800: 777541.952: [CMS-concurrent-abortable-preclean-start]

由于jvm并沒有把發(fā)生cms gc的原因給輸出郎哭,所以只能結(jié)合條件倒推下他匪。我在jvm群里咨詢了幾位大大想問下cms gc前后old區(qū)的大小變化,大大們的回答如下:

大大1:
看gc log夸研, initial mark 的記錄是cms 觸發(fā)時的used和capacity邦蜜,是之前的
final remark的記錄是之后的,我一般是這么理解的

大大2:
整個cms gc是從initial mark開始到remark結(jié)束亥至,initial mark告訴你觸發(fā)cms的old區(qū)大小悼沈,remark會告訴你cms后old區(qū)的使用情況

看了下日志:
**[GC [1 CMS-initial-mark: 950754K(2097152K)] ** 表示說開始gc之前old的大小為950754K,大約占整個old區(qū)的45%左右姐扮,但是結(jié)合 -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly 來看絮供,觸發(fā)這臺cms gc的原因不是old區(qū)內(nèi)存占用超過80%。

那么問題來了茶敏,觸發(fā)這次cms gc的原因到底是什么呢壤靶?咨詢了jvm群的大大,google了下外網(wǎng)和內(nèi)網(wǎng)文章惊搏。1.6版本的cms gc主要原因如下:

  • 老年代空間使用率超過設(shè)定值
  • 永久代空間使用率超過設(shè)定值
  • System.gc()調(diào)用并且設(shè)置了-XX:+ExplicitGCInvokesConcurrent 將一次full gc轉(zhuǎn)化為cms gc

目前已經(jīng)排除第一條贮乳,那么第二條是否滿足呢。需要注意的是perm區(qū)的閾值由-XX:CMSInitiatingPermOccupancyFraction指定恬惯,該值如果不設(shè)置的話是92向拆,使用jstat -gcutil觀察下使用情況如下:

    S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00  61.24  14.84  45.36  92.67  17167  771.384 14136 7486.615 8257.999
  0.00  61.24  14.99  45.36  92.67  17167  771.384 14136 7486.615 8257.999
  0.00  61.24  15.09  45.36  92.67  17167  771.384 14136 7486.615 8257.999
  0.00  61.24  15.19  45.36  92.67  17167  771.384 14136 7486.615 8257.999
  0.00  61.24  15.27  45.36  92.67  17167  771.384 14137 7486.821 8258.205

可以看到Perm區(qū)的占比為92.67超過默認值92,所以cms gc會一直被觸發(fā)酪耳。至于FGC次數(shù)為何那么大浓恳,可以參考R大的[文章](http://rednaxelafx.iteye.com/blog/1108768),cms 的initial-mark和remark都會計算到FGC次數(shù)里碗暗,所以jstat FGC次數(shù)可以理解為stw的次數(shù)吧奖蔓。

從上述日志基本可以確定這臺機器的頻繁gc原因是由于perm區(qū)占用率大導(dǎo)致的。

B機器

在分析了這臺機器是由于perm占用導(dǎo)致的cms gc后讹堤,我又查看了另外一臺機器吆鹤,發(fā)現(xiàn)了另外一個問題。
dump了另外一臺機器的gc log分析

檢測到的GC問題
篩選出了【9】條疑似問題的GC洲守,跳過了【4307】條可能正常的GC
存在【0】次Full GC及【3】次Mix GC(Mix GC相當于Full GC疑务,一次stop,多代進行回收)
【66.0%】【2/3】的Mix GC是因為晉升失敗導(dǎo)致的
存在【2】次晉升失敗的情況
過濾出來的GC中CMS GC比例占了【44%】【4/9】
調(diào)優(yōu)建議
預(yù)測到您可能沒有設(shè)置-XX:+UseCMSInitiatingOccupancyOnly梗醇,如果設(shè)置了請檢查-XX:CMSInitiatingOccupancyFraction的值是否小于83知允,如果這個條件滿足,說明老生代GC效果不給力叙谨,或者新生代GC過于頻繁晉升對象多温鸽,可嘗試調(diào)大CMSInitiatingOccupancyFraction或者調(diào)大老生代的大小

結(jié)合日志和之前的分析可疑看出來這臺機器的cms gc主要是由于老年代占用過大以及比較碎片化導(dǎo)致的。

問題解決

原因清楚了之后調(diào)整就比較簡單了,最終是將Xmx和Xms調(diào)整到了5g涤垫,將permsize和MaxPermSize調(diào)整到了384M姑尺。觀察了幾天沒有再出現(xiàn)頻繁cms gc的問題。

題外話

system gc

該機器的頻繁cms gc原因找到了蝠猬,再多分析一下cms gc的另外一個原因(System.gc)切蟋。

system gc主要有幾個原因:

  • rmi定時調(diào)用
  • 代碼調(diào)用 可以通過btrace或者arthas定位調(diào)用代碼
  • 堆外內(nèi)存清理
  • Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000

cms fail

concurrent mode failure

gc log會出現(xiàn)一些問題日志,表明cms遭遇了問題榆芦,首先就是‘concurrent mode failure’.

日志如下:

267.006: [GC 267.006: [ParNew: 629120K->629120K(629120K), 0.0000200 secs]
267.006: [CMS267.350: [CMS-concurrent-mark: 2.683/2.804 secs]
[Times: user=4.81 sys=0.02, real=2.80 secs]
(concurrent mode failure):
1378132K->1366755K(1398144K), 5.6213320 secs]
2007252K->1366755K(2027264K),
[CMS Perm : 57231K->57222K(95548K)], 5.6215150 secs]
[Times: user=5.63 sys=0.00, real=5.62 secs]
當minor gc發(fā)生了柄粹,但是old區(qū)中沒有足夠的空間容納需要晉升的對象時,cms會觸發(fā)一次full gc匆绣。此時STW驻右,所有應(yīng)用線程都會停止,old區(qū)中dead對象會被清理崎淳,上述日志中表明old區(qū)清理到了136MB堪夭,但是full gc STW了5.62s。這個操作是單線程進行的凯力,所以花費了很長時間茵瘾。

promotion failed

第二個問題就是當old區(qū)有足夠的空間給待晉升的對象時,但是由于空閑內(nèi)存過于碎片化導(dǎo)致的咐鹤。

6043.903: [GC 6043.903:
[ParNew (promotion failed): 614254K->629120K(629120K), 0.1619839 secs]
6044.217: [CMS: 1342523K->1336533K(2027264K), 30.7884210 secs]
2004251K->1336533K(1398144K),
[CMS Perm : 57231K->57231K(95548K)], 28.1361340 secs]
[Times: user=28.13 sys=0.38, real=28.13 secs]
日志表明拗秘,cms開始了一次ygc,而且認為old區(qū)中有足夠的空間來容納待晉升的對象(否則祈惶,將會導(dǎo)致concurrent mode failure問題)雕旨,然后由于old區(qū)內(nèi)存過于碎片化,導(dǎo)致cms無法晉升這些對象捧请。

所以凡涩,在minor gc的過程中,cms嘗試去清理和壓縮整理old區(qū)疹蛉。好消息是heap最終會被重新壓縮整理活箕,碎片化問題得以解決。但是會帶來一個28秒的停頓時間可款。這個時間比發(fā)生‘concurrent mode failure’時消耗的時間要長育韩,因為此時整個heap進行了壓縮整理,而concurrent mode failure只free掉old區(qū)的內(nèi)存闺鲸。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筋讨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子摸恍,更是在濱河造成了極大的恐慌悉罕,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異壁袄,居然都是意外死亡类早,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門然想,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莺奔,“玉大人欣范,你說我怎么就攤上這事变泄。” “怎么了恼琼?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵妨蛹,是天一觀的道長。 經(jīng)常有香客問我晴竞,道長蛙卤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任噩死,我火速辦了婚禮颤难,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘已维。我一直安慰自己行嗤,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布垛耳。 她就那樣靜靜地躺著栅屏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堂鲜。 梳的紋絲不亂的頭發(fā)上栈雳,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音缔莲,去河邊找鬼哥纫。 笑死,一個胖子當著我的面吹牛痴奏,可吹牛的內(nèi)容都是我干的蛀骇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼抛虫,長吁一口氣:“原來是場噩夢啊……” “哼松靡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起建椰,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤雕欺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屠列,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡啦逆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了笛洛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夏志。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苛让,靈堂內(nèi)的尸體忽然破棺而出沟蔑,到底是詐尸還是另有隱情,我是刑警寧澤狱杰,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布瘦材,位于F島的核電站,受9級特大地震影響仿畸,放射性物質(zhì)發(fā)生泄漏食棕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一错沽、第九天 我趴在偏房一處隱蔽的房頂上張望簿晓。 院中可真熱鬧,春花似錦千埃、人聲如沸憔儿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皿曲。三九已至,卻和暖如春吴侦,著一層夾襖步出監(jiān)牢的瞬間屋休,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工备韧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留劫樟,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓织堂,卻偏偏與公主長得像叠艳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子易阳,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容