1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【JVM性能優(yōu)化】面向CMS垃圾回收器的性能優(yōu)化方案

        共 3948字,需瀏覽 8分鐘

         ·

        2021-06-23 08:36

        前提概要

        如果沒有冬天,春天不會如此悅?cè)?;如果沒有偶爾的不幸,幸運不會如此受人歡迎。

        CMS垃圾回收的6個重要階段

        1. initial-mark 初始標記(CMS的第一個STW階段),標記GC Root直接引用的對象,GC Root直接引用的對象不多,所以很快。

        2. concurrent-mark并發(fā)標記階段,由第一階段標記過的對象出發(fā),所有可達的對象都在本階段標記。

        3. concurrent-preclean 并發(fā)預清理階段,也是一個并發(fā)執(zhí)行的階段。在本階段,會查找前一階段執(zhí)行過程中,[從新生代晉升或新分配或被更新的對象]。通過并發(fā)地重新掃描這些對象,預清理階段可以減少下一個stop-the-world 重新標記階段的工作量。

        4. concurrent-abortable-preclean,并發(fā)可中止的預清理階段。這個階段其實跟上一個階段做的東西一樣,也是為了減少下一個STW重新標記階段的工作量。增加這一階段是為了讓我們可以控制這個階段的結(jié)束時機,比如掃描多長時間(默認5秒)或者Eden區(qū)使用占比達到期望比例(默認50%)就結(jié)束本階段

        5. remark重標記階段(CMS的第二個STW階段),暫停所有用戶線程,從GC Root開始重新掃描整堆,標記存活的對象。需要注意的是,雖然CMS只回收老年代的垃圾對象,但是這個階段依然需要掃描新生代,因為很多GC Root都在新生代,而這些GC Root指向的對象又在老年代,這稱為“跨代引用”。

        6. concurrent-sweep ,并發(fā)清理。

        分析

        分析其GC日志,發(fā)現(xiàn)GC發(fā)生在CMS的收集階段

        • 箭頭1 顯示abortable-preclean階段耗時4.04秒。

        • 箭頭2 顯示的是remark階段,耗時0.11秒

        • 雖然abortable-preclean階段是concurrent的,不會暫停其他的用戶線程。就算不優(yōu)化,可能影響也不大。

        調(diào)優(yōu)之前先看下該應用的GC統(tǒng)計數(shù)據(jù),包括GC次數(shù),耗時:

        統(tǒng)計期間內(nèi)(18天)發(fā)生CMS GC 69次,其中abortable preclean階段平均耗時2.45秒,final remark階段平均112ms,最大耗時170ms。

        優(yōu)化目標

        降低abortable preclean時間,而且不增加final remark的時間(因為remark是STW的)。

        JVM參數(shù)調(diào)優(yōu)

        第一次調(diào)優(yōu)

        先嘗試調(diào)低abortable preclean階段的時間,看看效果。

        有兩個參數(shù)可以控制這個階段何時結(jié)束:

        -XX:CMSMaxAbortablePrecleanTime=5000
        復制代碼

        默認值5s,代表該階段最大的持續(xù)時間

        -XX:CMSScheduleRemarkEdenPenetration=50
        復制代碼

        默認值50%,代表Eden區(qū)使用比例超過50%就結(jié)束該階段進入remark

        調(diào)整為最大持續(xù)時間為1s,Eden區(qū)使用占比10%,如下:

        -XX:CMSMaxAbortablePrecleanTime=1000

        -XX:CMSScheduleRemarkEdenPenetration=10
        復制代碼

        為什么調(diào)整成這樣兩個值:首先每次CMS都發(fā)生在老年代使用占比達到80%時,因為這是由下面兩個參數(shù)決定的

        -XX:CMSInitiatingOccupancyFraction=80
        -XX:+UseCMSInitiatingOccupancyOnly
        復制代碼

        這兩個設(shè)置一般配合使用,一般用于『降低CMS GC頻率或者增加頻率、減少GC時長』的需求

        • -XX:CMSInitiatingOccupancyFraction=80 是指設(shè)定CMS在對內(nèi)存占用率達到80%的時候開始GC(因為CMS會有浮動垃圾,所以一般都較早啟動GC);

        • -XX:+UseCMSInitiatingOccupancyOnly標志來命令JVM不基于運行時收集的數(shù)據(jù)來啟動CMS垃圾收集周期。

        當該標志被開啟時,JVM通過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不僅僅是第一次。(否則后續(xù)會動態(tài)控制回收閾值)

        (慎用) 因此,只有當我們充足的理由(比如測試)并且對應用程序產(chǎn)生的對象的生命周期有深刻的認知時,才應該使用該標志。

        老年代的增長是由于部分對象在Minor GC后仍然存活,被晉升到老年代,導致老年代使用占比增長的,也就是在每次CMS GC發(fā)生之前剛剛發(fā)生過一次Minor GC所以在那一刻新生代的使用占比是很低的。

        那么我們預計這個時候盡快結(jié)束abortable preclean階段,在remark時就不需要掃描太多的Eden區(qū)對象,remark STW的時間也就不會太長。

        第一次調(diào)整參數(shù)

        在統(tǒng)計期間(17小時左右)內(nèi),發(fā)生過2次CMS GC。Abortable Preclean 平均耗時835ms,這是預期內(nèi)的。但是Final Remark 平均耗時495ms(調(diào)整前是112ms),其中一次是80ms,另一次是910ms!將近1秒鐘!Remark是STW的!對于要求低延時的應用來說這是無法接受的!

        [YG occupancy: 181274 K (1887488 K)] - 年輕代當前占用情況和總?cè)萘?/p>

        耗時80ms的這次remark發(fā)生時(早上9點,非高峰時段),新生代(YG)占用181.274M。

        remark耗時910ms的那次GC日志

        [YG occupancy: 773427 K (1887488 K)]

        耗時910ms的這次remark發(fā)生時(晚上10點左右,高峰時段),新生代(YG)占用773.427M。因為這個時候高峰期,新生代的占用量上升的非常快,幾乎同樣的時間內(nèi),非高峰時段僅上升到181M,但是高峰時段就上升到773M。

        • 如果abortale preclean階段時間太短,隨后在remark時,新生代占用越大,則remark持續(xù)的時間(STW)越長。

        • 不縮短abortale preclean耗時會出現(xiàn)過程gc;縮短的話,remark階段又會變長,而且是STW,更不能接受。

        對于這種情況,CMS提供了CMSScavengeBeforeRemark參數(shù),嘗試在remark階段之前進行一次Minor GC,以降低新生代的占用。

        第二次調(diào)優(yōu)

        增加 -XX:+CMSScavengeBeforeRemark 不是沒有代價的,因為這會增加一次Minor GC停頓。所以這個方案好或者不好的判斷標準就是:增加CMSScavengeBeforeRemark參數(shù)之后的minor GC停頓時間 + remark 停頓時間如果比增加之前的remark GC停頓時間要小,這才是好的方案。

        -XX:+CMSScavengeBeforeRemark:在CMS GC前啟動一次ygc,目的在于減少old gen對ygc gen的引用,降低remark時的開銷-----一般CMS的GC耗時 80%都在remark階段

        第二次調(diào)整的結(jié)果

        在統(tǒng)計期間(20小時左右)內(nèi),發(fā)生3次CMS GC。Abortable preclean 平均耗時693ms。Final remark平均耗時50ms,最大耗時60ms。Final remark的時間比調(diào)優(yōu)前的平均時間(112ms)更低。

        3次CMS GC remark前的Minor GC日志分析

        第1次是非高峰時段的表現(xiàn),Minor GC 耗時 0.01s + remark耗時 0.06s = 0.07s = 70ms,如下第2次是高峰時段,Minor GC 耗時 0.01s + remark耗時 0.05s = 0.06s = 60ms,如下第3次是非高峰時段,Minor GC 耗時 0.00s + remark耗時 0.04s = 0.04s = 40ms,如下

        所以,3次Minor GC + remark耗時的平均耗時 < 60ms,這比第一次調(diào)優(yōu)時remark平均耗時495ms好得多了

        總結(jié)

        解決abortable preclean 時間過長的方案可以歸結(jié)為兩步:

        縮短abortable preclean 時長,通過調(diào)整這兩個參數(shù):

        -XX:CMSMaxAbortablePrecleanTime=xxx

        -XX:CMSScheduleRemarkEdenPenetration=xxx
        復制代碼
        • 調(diào)整為多少的一個判斷標準是:abortable preclean階段結(jié)束時,新生代的空間占用不能大于某個參考值。在前面第一次調(diào)優(yōu)后,新生代(YG)占用181.274M,remark耗時80ms;新生代(YG)占用773.427M時,remark耗時910ms。所以這個參考值可以是300M。

        • 而如果新生代增長過快,像這次調(diào)優(yōu)應用2秒內(nèi)就能用光2G新生代堆空間的,就只能通過CMSScavengeBeforeRemark做一次Minor GC了。

        增加CMSScavengeBeforeRemark參數(shù)開啟remark前進行Minor GC的嘗試

        雖然官方說明這個增加這個參數(shù)是嘗試進行Minor GC,不一定會進行。但實際使用起來,幾乎每次remark前都會Minor GC。


        作者:李浩宇Alex
        鏈接:https://juejin.cn/post/6975125806305509389
        來源:掘金
        著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。



        瀏覽 88
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一道本在线视频 | 国产AV女主播 | 嬷嬷羞耻调教后宫h | free性欧美69 | 国产成人午夜精品A片21p漫画 | 亚洲av三区 | 欧美一级片在线 | 亚洲图片三区 | 二区三区四区视频 | 四虎综合网 |