<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          高并發(fā)場景下JVM調(diào)優(yōu)實踐之路

          共 8011字,需瀏覽 17分鐘

           ·

          2021-10-30 17:29

          作者:vivo互聯(lián)網(wǎng)技術(shù)團隊
          Li Guanyun、 Jessica Chen


          一、背景


          2021年2月,收到反饋,視頻APP某核心接口高峰期響應慢,影響用戶體驗。


          通過監(jiān)控發(fā)現(xiàn),接口響應慢主要是P99耗時高引起的,懷疑與該服務的GC有關(guān),該服務典型的一個實例GC表現(xiàn)如下圖:




          可以看出,在觀察周期里:

          • 平均每10分鐘Young GC次數(shù)66次,峰值為470次;

          • 平均每10分鐘Full GC次數(shù)0.25次,峰值5次;


          可見Full GC非常頻繁,Young GC在特定的時段也比較頻繁,存在較大的優(yōu)化空間。由于對GC停頓的優(yōu)化是降低接口的P99時延一個有效的手段,所以決定對該核心服務進行JVM調(diào)優(yōu)。


          二、優(yōu)化目標


          • 接口P99時延降低30%

          • 減少Young GC和Full GC次數(shù)、停頓時長、單次停頓時長


          由于GC的行為與并發(fā)有關(guān),例如當并發(fā)比較高時,不管如何調(diào)優(yōu),Young GC總會很頻繁,總會有不該晉升的對象晉升觸發(fā)Full GC,因此優(yōu)化的目標根據(jù)負載分別制定:


          目標1:高負載(單機1000 QPS以上)


          • Young GC次數(shù)減少20%-30% ,Young GC累積耗時不惡化;

          • Full GC次數(shù)減少50%以上,單次、累積Full GC耗時減少50%以上,服務發(fā)布不觸發(fā)Full GC。


          目標2:中負載(單機500-600)


          • Young GC次數(shù)減少20%-30% ,Young GC累積耗時減少20%;

          • Full GC次數(shù)不高于4次/天,服務發(fā)布不觸發(fā)Full GC。


          目標3:低負載(單機200 QPS以下)


          • Young GC次數(shù)減少20%-30% ,Young GC累積耗時減少20%;

          • Full GC次數(shù)不高于1次/天,服務發(fā)布不觸發(fā)Full GC。


          三、當前存在的問題


          當前服務的JVM配置參數(shù)如下:

          -Xms4096M -Xmx4096M -Xmn1024M-XX:PermSize=512M-XX:MaxPermSize=512M


          單純從參數(shù)上分析,存在以下問題:


          未顯示指定收集器?


          JDK 8默認搜集器為ParrallelGC,即Young區(qū)采用Parallel Scavenge,老年代采用Parallel Old進行收集,這套配置的特點是吞吐量優(yōu)先,一般適用于后臺任務型服務器。


          比如批量訂單處理、科學計算等對吞吐量敏感,對時延不敏感的場景,當前服務是視頻與用戶交互的門戶,對時延非常敏感,因此不適合使用默認收集器ParrallelGC,應選擇更合適的收集器。



          Young區(qū)配比不合理


          當前服務主要提供API,這類服務的特點是常駐對象會比較少,絕大多數(shù)對象的生命周期都比較短,經(jīng)過一次或兩次Young GC就會消亡。


          再看下當前JVM配置


          整個堆為4G,Young區(qū)總共1G,默認-XX:SurvivorRatio=8,即有效大小為0.9G,老年代常駐對象大小約400M。


          這就意味著,當服務負載較高,請求并發(fā)較大時,Young區(qū)中Eden + S0區(qū)域會迅速填滿,進而Young GC會比較頻繁。


          另外會引起本應被Young GC回收的對象過早晉升,增加Full GC的頻率,同時單次收集的區(qū)域也會增大,由于Old區(qū)使用的是ParralellOld,無法與用戶線程并發(fā)執(zhí)行,導致服務長時間停頓,可用性下降, P99響應時間上升。


          未設(shè)置

          -XX:MetaspaceSize和-XX:MaxMetaspaceSize

          Perm區(qū)在jdk?1.8已經(jīng)過時,被Meta區(qū)取代,因此-XX:PermSize=512M -XX:MaxPermSize=512M配置會被忽略,真正控制Meta區(qū)GC的參數(shù)為-XX:MetaspaceSize:Metaspace初始大小,64位機器默認為21M左右 -XX:MaxMetaspaceSize:Metaspace的最大值,64位機器默認為18446744073709551615Byte,可以理解為無上限 -XX:MaxMetaspaceExpansion:增大觸發(fā)metaspace GC閾值的最大要求 -XX:MinMetaspaceExpansion:增大觸發(fā)metaspace GC閾值的最小要求,默認為340784Byte


          這樣服務在啟動和發(fā)布的過程中,元數(shù)據(jù)區(qū)域達到21M時會觸發(fā)一次Full GC (Metadata GC Threshold),隨后隨著元數(shù)據(jù)區(qū)域的擴張,會夾雜若干次Full GC (Metadata GC Threshold),使服務發(fā)布穩(wěn)定性和效率下降。


          此外如果服務使用了大量動態(tài)類生成技術(shù)的話,也會因為這個機制產(chǎn)生不必要的Full GC (Metadata GC Threshold)。




          四、優(yōu)化方案/驗證方案


          上面已分析出當前配置存在的較為明顯的不足,下面優(yōu)化方案主要先針對性解決這些問題,之后再結(jié)合效果決定是否繼續(xù)深入優(yōu)化。


          當前主流/優(yōu)秀的搜集器包含:


          • Parrallel Scavenge + Parrallel Old:吞吐量優(yōu)先,后臺任務型服務適合;

          • ParNew + CMS:經(jīng)典的低停頓搜集器,絕大多數(shù)商用、延時敏感的服務在使用;

          • G1:JDK 9默認搜集器,堆內(nèi)存比較大(6G-8G以上)的時候表現(xiàn)出比較高吞吐量和短暫的停頓時間;

          • ZGC:JDK 11中推出的一款低延遲垃圾回收器,目前處在實驗階段;



          結(jié)合當前服務的實際情況(堆大小,可維護性),我們選擇ParNew + CMS方案是比較合適的。


          參數(shù)選擇的原則如下:


          1)Meta區(qū)域的大小一定要指定,且MetaspaceSize和MaxMetaspaceSize大小應設(shè)置一致,具體多大要結(jié)合線上實例的情況,通過jstat -gc可以獲取該服務線上實例的情況。

          # jstat -gc 31247S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT37888.0?37888.0?0.0?32438.5?972800.0?403063.5?3145728.0?2700882.3?167320.0?152285.0?18856.0?16442.4?15189?597.209?65?70.447?667.655


          可以看出MU在150M左右,

          因此-XX:MetaspaceSize=256M?

          -XX:MaxMetaspaceSize=256M是比較合理的。


          2)Young區(qū)也不是越大越好


          當堆大小一定時,Young區(qū)越大,Young GC的頻率一定越小,但Old區(qū)域就會變小,如果太小,稍微晉升一些對象就會觸發(fā)Full GC得不償失。


          如果Young區(qū)過小,Young GC就會比較頻繁,這樣Old區(qū)就會比較大,單次Full GC的停頓就會比較大。因此Young區(qū)的大小需要結(jié)合服務情況,分幾種場景進行比較,最終獲得最合適的配置。


          基于以上原則,以下為4種參數(shù)組合:


          1.ParNew +CMS,Young區(qū)擴大1倍

          -Xms4096M -Xmx4096M -Xmn2048M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC-XX:+CMSScavengeBeforeRemark


          2.ParNew +CMS,Young區(qū)擴大1倍,

          去除-XX:+CMSScavengeBeforeRemark

          (使用【-XX:CMSScavengeBeforeRemark】參數(shù)可以做到在重新標記前先執(zhí)行一次新生代GC)。


          因為老年代和年輕代之間的對象存在跨代引用,因此老年代進行GC Roots追蹤時,同樣也會掃描年輕代,而如果能夠在重新標記前先執(zhí)行一次新生代GC,那么就可以少掃描一些對象,重新標記階段的性能也能因此提升。)

          -Xms4096M -Xmx4096M -Xmn2048M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC


          3.ParNew +CMS,Young區(qū)擴大0.5倍

          -Xms4096M -Xmx4096M -Xmn1536M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark


          4.ParNew +CMS,Young區(qū)不變

          -Xms4096M -Xmx4096M -Xmn1024M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark


          下面,我們需要在壓測環(huán)境,對不同負載下4種方案的實際表現(xiàn)進行比較,分析,驗證。


          4.1 壓測環(huán)境驗證/分析


          高負載場景(1100?QPS)GC表現(xiàn)



          可以看出,在高負載場景,4種ParNew + CMS的各項指標表現(xiàn)均遠好于Parrallel Scavenge + Parrallel Old。其中:


          • 方案4(Young區(qū)擴大0.5倍)表現(xiàn)最佳,接口P95,P99延時相對當前方案降低50%,F(xiàn)ull GC累積耗時減少88%, Young GC次數(shù)減少23%,Young GC累積耗時減少4%,Young區(qū)調(diào)大后,雖然次數(shù)減少了,但Young區(qū)大了,單次Young GC的耗時也大概率會上升,這是符合預期的。


          • Young區(qū)擴大1倍的兩種方案,即方案2和方案3,表現(xiàn)接近,接口P95,P99延時相對當前方案降低40%,F(xiàn)ull GC累積耗時減少81%, Young GC次數(shù)減少43%,Young GC累積耗時減少17%,略遜于Young區(qū)擴大0.5倍,總體表現(xiàn)不錯,這兩個方案進行合并,不再區(qū)分。


          Young區(qū)不變的方案在新方案里,表現(xiàn)最差,淘汰。所以在中負載場景,我們只需要對比方案2和方案4。


          中負載場景(600?QPS)GC表現(xiàn)



          可以看出,在中負載場景,2種ParNew + CMS(方案2和方案4)的各項指標表現(xiàn)也均遠好于Parrallel Scavenge + Parrallel Old。


          • Young區(qū)擴大1倍的方案表現(xiàn)最佳,接口P95,P99延時相對當前方案降低32%,F(xiàn)ull GC累積耗時減少93%, Young GC次數(shù)減少42%,Young GC累積耗時減少44%;

          • Young區(qū)擴大0.5倍的方案稍遜一些。


          綜合來看,兩個方案表現(xiàn)十分接近,原則上兩種方案都可以,只是Young區(qū)擴大0.5倍的方案在業(yè)務高峰期的表現(xiàn)更佳,為盡量保證高峰期服務的穩(wěn)定和性能,目前更傾向于選擇ParNew + CMS,Young區(qū)擴大0.5倍方案。


          4.2 灰度方案/分析


          為保證覆蓋業(yè)務的高峰期,選擇周五、周六、周日分別從兩個機房隨機選擇一臺線上實例,線上實例的指標符合預期后,再進行全量升級。


          目標組? xx.xxx.60.6


          采用方案2,即目標方案

          -Xms4096M -Xmx4096M -Xmn1536M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark


          對照組1? xx.xxx.15.215


          采用原始方案

          -Xms4096M -Xmx4096M -Xmn1024M-XX:PermSize=512M-XX:MaxPermSize=512M


          對照組2? xx.xxx.40.87


          采用方案4,即候選目標方案

          -Xms4096M -Xmx4096M -Xmn2048M-XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark


          灰度3臺機器。


          我們先分析下Young GC相關(guān)指標:


          Young GC次數(shù)



          Young GC累計耗時



          Young GC單次耗時



          可以看出,與原始方案相比,目標方案的YGC次數(shù)減少50%,累積耗時減少47%,吞吐量提升的同時,服務停頓的頻率大大降低,而代價是單次Young GC的耗時增長3ms,收益是非常高的。


          對照方案2即Young區(qū)2G的方案整體表現(xiàn)稍遜與目標方案,再分析Full GC指標。


          老年代內(nèi)存增長情況



          Full GC次數(shù)



          Full GC累計/單次耗時



          與原始方案相比,使用目標方案時,老年代增長的速度要緩慢很多,基本在觀測周期內(nèi)Full GC發(fā)生的次數(shù)從155次減少至27次,減少82%,停頓時間均值從399ms減少至60ms,減少85%,毛刺也非常少。


          對照方案2即Young區(qū)2G的方案整體表現(xiàn)遜于目標方案。到這里,可以看出,目標方案從各個維度均遠優(yōu)于原始方案,調(diào)優(yōu)目標也基本達成。


          但細心的同學會發(fā)現(xiàn),目標方案相對原始方案,"Full GC"(實際上是CMS Background GC)耗時更加平穩(wěn),但每個若干次"Full GC"后會有一個耗時很高的毛刺出現(xiàn),這意味這個用戶請求在這個時刻會停頓2-3s,能否進一步優(yōu)化,給用戶一個更加極致的體驗呢?



          4.3 再次優(yōu)化


          這里首先要分析這現(xiàn)象背后的邏輯。



          對于CMS搜集器,采用的搜集算法為Mark-Sweep-[Compact]。


          CMS搜集器GC的種類:


          CMS Background GC


          這種GC是CMS最常見的一類,是周期性的,由JVM的常駐線程定時掃描老年代的使用率,當使用率超過閾值時觸發(fā),采用的是Mark-Sweep方式,由于沒有Compact這種耗時操作,且可以與用戶進程并行,所以CMS的停頓會比較低,GC日志中出現(xiàn)GC (CMS Initial Mark)字樣就代表發(fā)生了一次CMS Background GC。


          Background GC由于采用的是Mark-Sweep,會導致老年代內(nèi)存碎片,這也是CMS最大的弱點。


          CMS Foreground GC


          這種GC是CMS搜集器里真正意義上的Full GC,采用Serial Old或Parralel Old進行收集,出現(xiàn)的頻率就較低,當往往出現(xiàn)后就會造成較大的停頓。


          觸發(fā)CMS Foreground GC的場景有很多,場景的如下:


          • System.gc();

          • jmap -histo:live pid;

          • 元數(shù)據(jù)區(qū)域空間不足;

          • 晉升失敗,GC日志中的標志為ParNew(promotion failed);

          • 并發(fā)模式失敗,GC日志中的標志為councurrent mode failure字樣。


          不難推斷,目標方案中的毛刺是晉升失敗或并發(fā)模式失敗造成的,由于線上沒有開啟打印gc日志,但也無妨,因為這兩種場景的根因是一致的,就是若干次CMS Backgroud GC后造成的老年代內(nèi)存碎片。


          我們只需要盡可能減少由于老年代碎片觸發(fā)晉升失敗、并發(fā)模式失敗即可。


          CMS Background GC由JVM的常駐線程定時掃描老年代的使用率,當使用率超過閾值時觸發(fā),該閾值由-XX:CMSInitiatingOccupancyFraction;?

          -XX:+UseCMSInitiatingOccupancyOnly兩個參數(shù)控制,不設(shè)置,默認首次為92%,后續(xù)會根據(jù)歷史情況進行預測,動態(tài)調(diào)整。


          如果我們固定閾值的大小,將該閾值設(shè)置為一個相對合理的值,既不使GC過于頻繁,又可以降低晉升失敗或并發(fā)模式失敗的概率,就可以大大緩解毛刺產(chǎn)生的頻率。


          目標方案的堆分布如下:

          • Young區(qū) 1.5G

          • Old區(qū) 2.5G

          • Old區(qū)常駐對象 約400M


          按經(jīng)驗數(shù)據(jù),75%,80%是比較折中的,因此我們選擇-XX:CMSInitiatingOccupancyFraction=75 -

          XX:+UseCMSInitiatingOccupancyOnly進行灰度觀察(我們也對80%的場景做了對照實驗,75%優(yōu)于80%)。


          最終目標方案的配置為:

          -Xms4096M -Xmx4096M -Xmn1536M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly


          如上配置,灰度 xx.xxx.60.6 一臺機器;



          從再次優(yōu)化的結(jié)果上看,CMS Foreground GC引起的毛刺基本消失,符合預期。


          因此,視頻服務最終目標方案的配置為;

          -Xms4096M?-Xmx4096M?-Xmn1536M?-XX:MetaspaceSize=256M?-XX:MaxMetaspaceSize=256M?-XX:+UseParNewGC?-XX:+UseConcMarkSweepGC?-XX:+CMSScavengeBeforeRemark?-XX:CMSInitiatingOccupancyFraction=75?-XX:+UseCMSInitiatingOccupancyOnly


          五、結(jié)果驗收


          灰度持續(xù)7天左右,覆蓋工作日與周末,結(jié)果符合預期,因此符合在線上開啟全量的條件,下面對全量后的結(jié)果進行評估。


          Young GC次數(shù)



          Young GC累計耗時



          單次Young GC耗時



          從Young GC指標上看,調(diào)整后Young GC次數(shù)平均減少30%,Young GC累積耗時平均減少17%,Young GC單次耗時平均增加約7ms,Young GC的表現(xiàn)符合預期。


          除了技術(shù)手段,我們也在業(yè)務上做了一些優(yōu)化,調(diào)優(yōu)前實例的Young GC會出現(xiàn)明顯的、不規(guī)律的(定時任務不一定分配到當前實例)毛刺,這里是業(yè)務上的一個定時任務,會加載大量數(shù)據(jù),調(diào)優(yōu)過程中將該任務進行分片,分攤到多個實例上,進而使Young GC更加平滑。


          Full GC單次/累積耗時




          從"Full GC"的指標上看,"Full GC"的頻率、停頓極大減少,可以說基本上沒有真正意義上的Full GC了。


          核心接口-A (下游依賴較多) P99響應時間,減少19%(從 3457 ms下降至 2817 ms);



          核心接口-B (下游依賴中等)? P99響應時間,減少41%(從 1647ms下降至 973ms);



          核心接口-C (下游依賴最少) P99響應時間,減少80%(從 628ms下降至 127ms);



          綜合來看,整個結(jié)果是超出預期的。Young GC表現(xiàn)與設(shè)定的目標非常吻合,基本上沒有真正意義上的Full GC,接口P99的優(yōu)化效果取決于下游依賴的多少,依賴越少,效果越明顯。


          六、寫在最后


          由于GC算法復雜,影響GC性能的參數(shù)眾多,并且具體參數(shù)的設(shè)置又取決于服務的特點,這些因素都很大程度增加了JVM調(diào)優(yōu)的難度。


          本文結(jié)合視頻服務的調(diào)優(yōu)經(jīng)驗,著重介紹調(diào)優(yōu)的思路和落地過程,同時總結(jié)出一些通用的調(diào)優(yōu)流程,希望能給大家提供一些參考。


          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99国产性爱 | 亚洲人人操 | 珍藏3年极品人妻疯狂3p | 色婷婷中文在线观看视频 | 无码高清电影 |