CMS的缺點分析
CMS是一款優(yōu)秀的收集器, 它最主要的優(yōu)點在名字上已經(jīng)體現(xiàn)出來:并發(fā)收集熏迹、 低停頓檐薯, 一些官方公開文檔里面也稱之為“并發(fā)低停頓收集器”(Concurrent Low Pause Collector) 。CMS收集器是HotSpot虛擬機追求低停頓的第一次成功嘗試注暗, 但是它還遠達不到完美的程度, 至少有以下三個明顯的缺點:
1.并發(fā)導致CPU資源緊張
首先墓猎, CMS收集器對處理器資源非常敏感捆昏。事實上, 面向并發(fā)設計的程序都對處理器資源比較敏感毙沾。在并發(fā)階段骗卜, 它雖然不會導致用戶線程停頓, 但卻會因為占用了一部分線程(或者說處理器的計算能力) 而導致應用程序變慢, 降低總吞吐量寇仓。
CMS默認啟動的回收線程數(shù)是(處理器核心數(shù)量+3) /4举户, 也就是說, 如果處理器核心數(shù)在四個或以上遍烦, 并發(fā)回收時垃圾收集線程只占用不超過25%的處理器運算資源俭嘁, 并且會隨著處理器核心數(shù)量的增加而下降。但是當處理器核心數(shù)量不足四個時服猪,CMS對用戶程序的影響就可能變得很大供填。如果應用本來的處理器負載就很高, 還要分出一半的運算能力去執(zhí)行收集器線程罢猪, 就可能導致用戶程序的執(zhí)行速度忽然大幅降低近她。
比如我們常見的機器是2核4G,那么分配給CMS的回收線程數(shù)= (2+3)/4 =1 個膳帕,直接占據(jù)了一半的CPU資源
因此CMS帶來的第一個問題就是影響CPU的資源使用粘捎,特別是在本身CPU核數(shù)就少的情況下。
2.Con-current Mode Failure問題
由于CMS收集器無法處理“浮動垃圾”(Floating Garbage) 危彩, 有可能出現(xiàn)“Con-current Mode Failure”失敗進而導致另一次完全“Stop The World”的Full GC的產(chǎn)生攒磨。
在CMS的并發(fā)標記和并發(fā)清理階段, 用戶線程是還在繼續(xù)運行的恬砂, 程序在運行自然就還會伴隨有新的垃圾對象不斷產(chǎn)生咧纠, 但這一部分垃圾對象是出現(xiàn)在標記過程結(jié)束以后, CMS無法在當次收集中處理掉它們泻骤, 只好留待下一次垃圾收集時再清理掉漆羔。這一部分垃圾就稱為“浮動垃圾”。
由于在垃圾收集階段用戶線程還需要持續(xù)運行狱掂, 那就還需要預留足夠內(nèi)存空間提供給用戶線程使用演痒, 因此CMS收集器不能像其他收集器那樣等待到老年代幾乎完全被填滿了再進行收集, 必須預留一部分空間供并發(fā)收集時的程序運作使用趋惨。
在JDK5的默認設置下鸟顺, CMS收集器當老年代使用了68%的空間后就會被激活, 這是一個偏保守的設置器虾, 如果在實際應用中老年代增長并不是太快讯嫂, 可以適當調(diào)高參數(shù)-XX:CMSInitiatingOccu-pancyFraction的值來提高CMS的觸發(fā)百分比, 降低內(nèi)存回收頻率兆沙, 獲取更好的性能欧芽。到了JDK 6時, CMS收集器的啟動閾值就已經(jīng)默認提升至92%葛圃。
但這又會更容易面臨另一種風險:要是CMS運行期間預留的內(nèi)存無法滿足程序分配新對象的需要千扔, 就會出現(xiàn)一次“并發(fā)失敗”(Concurrent Mode Failure) 憎妙, 這時候虛擬機將不得不啟動后備預案:凍結(jié)用戶線程的執(zhí)行, 臨時啟用Serial Old收集器來重新進行老年代的垃圾收集曲楚,但這樣停頓時間就很長了厘唾。所以參數(shù)-XX:CMSInitiatingOccupancyFraction設置得太高將會很容易導致大量的并發(fā)失敗產(chǎn)生, 性能反而降低龙誊, 用戶應在生產(chǎn)環(huán)境中根據(jù)實際應用情況來權(quán)衡設置抚垃。
3.內(nèi)存碎片問題
最后一個缺點, 在本節(jié)的開頭曾提到载迄, CMS是一款基于“標記-清除”算法實現(xiàn)的收集器讯柔, 如果讀者對前面這部分介紹還有印象的話, 就可能想到這意味著收集結(jié)束時會有大量空間碎片產(chǎn)生护昧。(如下紅圈所示)
空間碎片過多時魂迄, 將會給大對象分配帶來很大麻煩, 往往會出現(xiàn)老年代還有很多剩余空間惋耙, 但就是無法找到足夠大的連續(xù)空間來分配當前對象捣炬, 而不得不提前觸發(fā)一次Full GC的情況。
為了解決這個問題绽榛,CMS收集器提供了一個-XX:+UseCMS-CompactAtFullCollection開關(guān)參數(shù)(默認是開啟的湿酸, 此參數(shù)從JDK 9開始廢棄) , 用于在CMS收集器不得不進行Full GC時開啟內(nèi)存碎片的合并整理過程灭美, 由于這個 內(nèi)存整理必須移動存活對象推溃, (在Shenandoah和ZGC出現(xiàn)前) 是無法并發(fā)的。這樣空間碎片問題是解 決了届腐, 但停頓時間又會變長铁坎, 因此虛擬機設計者們還提供了另外一個參數(shù)-XX:CMSFullGCsBeforeCompaction(此參數(shù)從JDK 9開始廢棄) , 這個參數(shù)的作用是要求CMS收集器在執(zhí)行過若干次(數(shù)量由參數(shù)值決定) 不整理空間的Full GC之后犁苏, 下一次進入Full GC前會先進行碎片整理(默認值為0硬萍, 表 示每次進入Full GC時都進行碎片整理) 。