日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

九、CMS

發布時間:2024/1/18 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 九、CMS 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、基礎概念

  • cms是以優化停頓時間為目標的一個垃圾收集器
  • 采用的是標記-清除算法
  • 其實CMS嚴格意義是來說是young區和old區一起回收的
  • 流程如下:
  • 并行(jdk1.8以后)初始標記,用于標記GC ROOT以及第一個相連的對象(STW,時間短)
  • 并發標記,跟用戶線程一起行(時間長)
  • 并行重新標記,主要是防止多標和漏標的情況(時間短)
  • 并發清理(時間長),這個時候用戶產生的垃圾就放到下一次回收

二、Backgroud CMS(正常模式)

  • 這種情況下其實我們的GC流程還可以細分
    • 并行初始標記
    • 并發標記
      • 并發預處理
      • 可終止的預處理
    • 并行重新標記
    • 并發清理
  • 為什么會多出來兩個流程呢?
    • 主要是為了防止young區引用了old區的對象。假設young區引用了old對象你咋辦,你要全量掃描一次young嗎,慢死你?那就進行一次minorGC
    • 并發預處理:主要是在并行重新標記的時候,這個動作是STW,所以并發預處理就是提前先干一次這個活,去標記從young區晉升上來的對象以及old區引用改變的對象
    • 可終止的預處理:主要是堆young區進行掃描,看那些對象引用了old區的對象,然后保存在卡表中(流程在4、卡表中),然后這個時候remark的時候就很爽了。
  • CMSScheduleRemarkEdenSizeThreshold 默認值:2M
    CMSScheduleRemarkEdenPenetration 默認值:50%
    • 這兩個參數的意思是:當Eden區超過2M的時候開始,當Eden區的內存超過50%就停止可終止的預處理。
    • 為什么會設置這么2個參數?
      • 主要是想要在Remark的前面發生一次minor GC這個時候卡表里面的數據就會很少了。
      • 所以還有一個參數默認是5s就是說當超過5s也會終止。或者如果發生了一次young GC也會終止。

三、記憶集

  • 進行young GC時,GC Root除了常見的棧引用、常量、靜態變量、JNI、Class對象等,
  • 作用:
    • 老年代如果引用了新生代的對象,這種對象也應該要加入到我們的GC Root里面去,因為你進行young GC的時候,總不能全量掃描一遍old區把,這太扯淡了。所以就用記憶集這種數據結構保存這種引用關系。
    • 新生代引用了老年代的對象,道理相反
  • 記憶集保存非收集對象到收集對象的引用關系的集合。
  • 記憶集只是一種思想

四、卡表

  • 卡表是記憶集的實現
  • 在hotSpot中卡表是一個字節數組,數組中每一項對應內存中某一塊連續的區域。如果區域中的某一個對象引用了待回收的區域的對象,就將這個數組中對應的元素變為1,否則為0
  • 卡表是使用一個字節數組實現:CARD_TABLE[],每個元素對應著其標識的內存區域一塊特定大小的內存塊,稱為"卡頁"。hotSpot使用的卡頁是2^9大小,即512字節
  • 一個卡頁中可包含多個對象,只要有一個對象的字段存在跨代指針,其對應的卡表的元素標識就變成1,表示該元素變臟,否則為0。GC時,只要篩選本收集區的卡表中變臟的元素加入GC Roots里。
  • 流程:
  • 并發標記的時候,發現young區里面有引用old區的對象,然后這個A所在的區域標記為臟卡
  • 重新標記的時候,就知道這個是被用了的,然后變成正常卡


五、Foregroud CMS(特殊模式)

  • CMS的另一種收集模式,只有在正常模式的并發標記階段失敗的時候,才會走到這個模式下
  • 什么是并發失敗?
    • 在老年代填滿之前不能完成old區的不可達對象的回收。因為這樣話新來的對象是不能放入Old區的,OOM了
    • 老年代的有效內存空間不能滿足晉升的需要
    • 說直白一點:就是并發標記的時候,內存不足了,OOM了
  • 怎樣可以降低并發失敗的幾率呢?
    • 通過設置下面的參數,讓old的內存達到一定比例我們就開始回收了,而不是等他滿了在回收
    • -XX:CMSInitiatingOccupancyFraction
    • -XX:+UseCMSInitiatingOccupancyOnly
    • 注意:-XX:+UseCMSInitiatingOccupancyOnly 只是用設定的回收閾值(上面指定的70%),如果不指定,JVM僅在第一次使用設定值,后續則自動調整.這兩個參數表示只有在Old區占了CMSInitiatingOccupancyFraction設置的百分比的內存時才滿足觸發CMS的條件。注意這只是滿足觸發CMS GC的條件。至于什么時候真正觸發CMS GC,由一個后臺掃描線程決定。CMSThread默認2秒鐘掃描一次,判斷是否需要觸發CMS,這個參數可以更改這個掃描時間間隔。
  • 如果真出現了并發失敗怎么解決呢?
    • 碎片問題也是CMS采用的標記清理算法最讓人詬病的地方:Backgroud CMS采用的標記清理算法會導致內存碎片問題,從而埋下發生FullGC導致長時間STW的隱患
    • 通過以下的參數控制,開始進行標記-整理算法。
      • -XX:+UseCMSCompactAtFullCollection
      • -XX:CMSFullGCsBeforeCompaction=0
      • 這兩個參數表示多少次FullGC后采用MSC算法壓縮堆內存,0表示每次FullGC后都會壓縮,同時0也是默認值
    • 如果不開啟上面的參數,那就是標記-清除算法

六、三色標記

  • 當并發標記的時候,業務線程其實也還在運行,這個時候就有可能產生多標和漏標的情況
  • 用三色標記法把GC root可達性標記對象的時候,對于“是否標記”來分顏色
  • 三色標記其實官方并沒有這個說法,也沒有這個概念,這個只是因為你看不懂cms源碼,自己YY出來的一個東東

黑色

  • 當前對象已經被掃描,且它鏈上所有的對象也都被掃描了
  • 黑色對象表示應被掃描完成了,是可以存活的安全對象
  • 黑色不能跳過灰色直接指向白色

灰色

  • 當前對象已經被掃描,在鏈上它的下一個對象沒有被掃描

白色

  • 這個對象沒有被掃描
  • 可達性分析之前,所有的對象都是白色
  • 可達性分析完了之后,還是白色話就證明你是垃圾

流程

  • 初始時,還沒有開始進行可達性標記,所有對象都是白色的
  • 開始可達性標記,首先是GC root直接引用的對象變成灰色
  • 從灰色的集合中獲取對象
  • 將灰色集合中的對象變成黑色,并且將該對象的引用其他對象變成灰色
  • 重復執行3,4步,直到灰色集合變空
  • 剩下的白色對象就是垃圾了

  • 多標-浮動垃圾

    • 因為并發標記,當GC線程將某個對象變成黑色后,還沒進入到重新標記階段,業務線程不引用這個對象了。但是這個對象已經是黑色了。
    • 并發標記開始后,產生的堆內的垃圾,也會直接變成黑色,不做回收
    • 多標,并不會影響正確性,其實還好,下次進行回收就行了。問題不大

    漏標

    • 因為并發標記,當某個灰色對象引用的白色對象斷開,然后這個白色對象又被黑色對象引用了。那么這個白色對象就成垃圾了,不會被染色了,因為黑色對象是不會在進行掃描標記的。
    • 解決方案
      • 快照的方式:就是灰色斷開的時候,快照一下,將灰色對象與白色對象的引用保存起來,然后重新標記的時候,就將灰色的為根在標記,這樣把白色的就能變成黑的了
      • 增量的方式:當黑色引用白色,就將黑色變成灰色,等待重新標記掃描
    • 以上無論是對引用關系記錄的插入還是刪除, 虛擬機的記錄操作都是通過寫屏障實現的。
      • 寫屏障實現原始快照(SATB): 當對象B的成員變量的引用發生變化時,比如引用消失(a.b.d = null),我們可以利用寫屏障,將B原來成員變量的引用對象D記錄下來
      • 寫屏障實現增量更新: 當對象A的成員變量的引用發生變化時,比如新增引用(a.d = d),我們可以利用寫屏障,將A新的成員變量引用對象D 記錄下來

    七、調優參數

    CMS標記清除的全局整理

    • 由于CMS使用的是標記清除算法,而標記清除算法會有大量的內存碎片的產生,所以JVM提供了
      -XX:+UseCMSCompactAtFullCollection參數用于在全局GC(full GC)后進行一次碎片整理的工作
    • 由于每次全局GC后都進行碎片整理會較大的影響停頓時間,JVM又提供了參數
      -XX:CMSFullGCsBeforeCompaction去 控制在幾次全局GC后會進行碎片整理

    CMS常用參數含義

    • -XX:+UseConcMarkSweepGC
      打開CMS GC收集器。JVM在1.8之前默認使用的是Parallel GC,9以后使用G1 GC。
    • -XX:+UseParNewGC
      當使用CMS收集器時,默認年輕代使用多線程并行執行垃圾回收(UseConcMarkSweepGC開啟后則默認開啟)。
    • -XX:+CMSParallelRemarkEnabled
      采用并行標記方式降低停頓(默認開啟)。
    • -XX:+CMSConcurrentMTEnabled
      被啟用時,并發的CMS階段將以多線程執行(因此,多個GC線程會與所有的應用程序線程并行工作)。(默認開啟)
    • -XX:ConcGCThreads
      定義并發CMS過程運行時的線程數。
    • -XX:ParallelGCThreads
      定義CMS過程并行收集的線程數。
    • -XX:CMSInitiatingOccupancyFraction
      該值代表老年代堆空間的使用率,默認值為68。當老年代使用率達到此值之后,并行收集器便開始進行垃圾收集,該參數需要配合UseCMSInitiatingOccupancyOnly一起使用,單獨設置無效。
    • -XX:+UseCMSInitiatingOccupancyOnly
      該參數啟用后,參數CMSInitiatingOccupancyFraction才會生效。默認關閉。
    • -XX:+CMSClassUnloadingEnabled
      相對于并行收集器,CMS收集器默認不會對永久代進行垃圾回收。如果希望對永久代進行垃圾回收,可用設置-XX:+CMSClassUnloadingEnabled。默認關閉。
    • -XX:+CMSIncrementalMode
      開啟CMS收集器的增量模式。增量模式使得回收過程更長,但是暫停時間往往更短。默認關閉。
    • -XX:CMSFullGCsBeforeCompaction
      設置在執行多少次Full GC后對內存空間進行壓縮整理,默認值0。
    • -XX:+CMSScavengeBeforeRemark
      在cms gc remark之前做一次ygc,減少gc roots掃描的對象數,從而提高remark的效率,默認關閉。
    • -XX:+ExplicitGCInvokesConcurrent
      該參數啟用后JVM無論什么時候調用系統GC,都執行CMS GC,而不是Full GC。
    • -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
      該參數保證當有系統GC調用時,永久代也被包括進CMS垃圾回收的范圍內。
    • -XX:+DisableExplicitGC
      該參數將使JVM完全忽略系統的GC調用(不管使用的收集器是什么類型)。
    • -XX:+UseCompressedOops
      這個參數用于對類對象數據進行壓縮處理,提高內存利用率。(默認開啟)
    • -XX:MaxGCPauseMillis=200
      這個參數用于設置GC暫停等待時間,單位為毫秒,不要設置過低。

    CMS的線程數計算公式

    分young區的parnew gc線程數和old區的cms線程數,分別為以下兩參數:

    • -XX:ParallelGCThreads=m // STW暫停時使用的GC線程數,一般用滿CPU
    • -XX:ConcGCThreads=n // GC線程和業務線程并發執行時使用的GC線程數,一般較小

    ParallelGCThreads

    其中ParallelGCThreads 參數的默認值是:
    CPU核心數 <= 8,則為 ParallelGCThreads=CPU核心數,比如4C8G取4,8C16G取8
    CPU核心數 > 8,則為 ParallelGCThreads = CPU核心數 * 5/8 + 3 向下取整
    16核的情況下,ParallelGCThreads = 13
    32核的情況下,ParallelGCThreads = 23
    64核的情況下,ParallelGCThreads = 43
    72核的情況下,ParallelGCThreads = 48

    ConcGCThreads
    ConcGCThreads的默認值則為:
    ConcGCThreads = (ParallelGCThreads + 3)/4 向下取整。
    ParallelGCThreads = 1~4時,ConcGCThreads = 1
    ParallelGCThreads = 5~8時,ConcGCThreads = 2
    ParallelGCThreads = 13~16時,ConcGCThreads = 4

    推薦配置

    第一種情況:8C16G左右服務器,再大的服務器可以上G1了 沒必要
    -Xmx12g -Xms12g
    -XX:ParallelGCThreads=8
    -XX:ConcGCThreads=2
    -XX:+UseConcMarkSweepGC
    -XX:+CMSClassUnloadingEnabled
    -XX:+CMSIncrementalMode
    -XX:+CMSScavengeBeforeRemark
    -XX:+UseCMSInitiatingOccupancyOnly
    -XX:CMSInitiatingOccupancyFraction=70
    -XX:CMSFullGCsBeforeCompaction=5
    -XX:MaxGCPauseMillis=100 // 按業務情況來定
    -XX:+ExplicitGCInvokesConcurrent
    -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps

    第二種情況:4C8G
    -Xmx6g -Xms6g
    -XX:ParallelGCThreads=4
    -XX:ConcGCThreads=1
    -XX:+UseConcMarkSweepGC
    -XX:+CMSClassUnloadingEnabled
    -XX:+CMSIncrementalMode
    -XX:+CMSScavengeBeforeRemark
    -XX:+UseCMSInitiatingOccupancyOnly
    -XX:CMSInitiatingOccupancyFraction=70
    -XX:CMSFullGCsBeforeCompaction=5
    -XX:MaxGCPauseMillis=100 // 按業務情況來定
    -XX:+ExplicitGCInvokesConcurrent
    -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps

    第三種情況:2C4G,這種情況下,也不推薦使用,因為2C的情況下,線程上下文的開銷比較大,性能可能還不如你不動的情況,沒必要。非要用,給你個配置,你自己玩。
    -Xmx3g -Xms3g
    -XX:ParallelGCThreads=2
    -XX:ConcGCThreads=1
    -XX:+UseConcMarkSweepGC
    -XX:+CMSClassUnloadingEnabled
    -XX:+CMSIncrementalMode
    -XX:+CMSScavengeBeforeRemark
    -XX:+UseCMSInitiatingOccupancyOnly
    -XX:CMSInitiatingOccupancyFraction=70
    -XX:CMSFullGCsBeforeCompaction=5
    -XX:MaxGCPauseMillis=100 // 按業務情況來定
    -XX:+ExplicitGCInvokesConcurrent
    -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps

    八、JDK為什么不選用CMS做垃圾收集器?

    • CMS單線程或者雙線程時,效率很低
    • CMS可能會導致并發失敗,從而引起full gc
    • CMS可終止的預處理在極限狀態會導致5s的STW
    • CMS只是針對于停頓時間,在吞吐量上并不是很友好

    總結

    以上是生活随笔為你收集整理的九、CMS的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。