爲何不建議<=3G的狀況下使用CMS GC

以前曾經有講過在heap size<=3G的狀況下徹底不要考慮CMS GC,在heap size>3G的狀況下也優先選擇ParallelOldGC,而不是CMS GC,只有在暫停時間沒法接受的狀況下才考慮CMS GC(不過固然,通常來講在heap size>8G後基本上都得選擇CMS GC,不然那暫停時間是至關嚇人的,除非是徹底不在意響應時間的應用),這其實也是官方的建議(每一年JavaOne的GC Tuning基本都會這麼講)。 html

爲何給了一個這麼「武斷」的建議呢,不是我對CMS GC有什麼不爽,相反CMS GC一直是我很熱愛的一種GC實現,之因此建議在<=3G的狀況下徹底不要考慮CMS GC,主要出於如下幾點考慮: 一、觸發比率很差設置 在JDK 1.6的版本中CMS GC的觸發比率默認爲old使用到92%時,假設3G的heap size,那麼意味着舊生代大概就在1.5G--2.5G左右的大小,假設是92%觸發,那麼意味着這個時候舊生代只剩120M--200M的大小,一般 這點大小頗有多是會致使不夠裝下新生代晉生的對象,所以須要調整觸發比率,但因爲heap size比較小,這個時候到底設置爲多少是挺難設置的,例如我看過heap size只有1.5G,old才800m的狀況下,還使用CMS GC的,觸發比率仍是80%,這種狀況下就悲催了,意味着舊生代只要使用到640m就觸發CMS GC,只要應用裏稍微把一些東西cache了就會形成頻繁的CMS GC。 CMS GC是一個大部分時間不暫停應用的GC,就形成了須要給CMS GC留出必定的時間(由於大部分時間不暫停應用,這也意味着整個CMS GC過程的完成時間是會比ParallelOldGC時的一次Full GC長的),以便它在進行回收時內存別分配滿了,而heap size原本就小的狀況下,留多了嘛容易形成頻繁的CMS GC,留少了嘛會形成CMS GC還在進行時內存就不夠用了,而在不夠用的狀況下CMS GC會退化爲採用Serial Full GC來完成回收動做,這個時候就慢的離譜了。 二、搶佔CPU CMS GC大部分時間和應用是併發的,因此會搶佔應用的CPU,一般在CMS GC較頻繁的狀況下,能夠很明顯看到一個CPU會消耗的很是厲害。 三、YGC速度變慢 因爲CMS GC的實現原理,致使對象重新生代晉升到舊生代時,尋找哪裏能放下的這個步驟比ParallelOld GC是慢一些的,所以就致使了YGC速度會有必定程度的降低。 四、碎片問題帶來的嚴重後果 CMS GC最麻煩的問題在於碎片問題,一樣是因爲實現原理形成的,CMS GC爲了確保儘量少的暫停應用,取消了在回收對象所佔的內存空間後Compact的過程,所以就形成了在回收對象後整個old區會造成各類各樣的不連續 空間,天然也就產生了不少的碎片,碎片會形成什麼後果呢,會形成例如明明舊生代還有4G的空餘空間,而新生代就算所有是存活的1.5g對象,也仍是會出現 promotion failed的現象,而在出現這個現象的狀況下CMS GC多數會採用Serial Full GC來解決問題。 碎片問題最麻煩的是你徹底不知道它何時會出現,所以有可能會形成某天高峯期的時候應用忽然來了個長暫停,因而就悲催了,對於不少採用了相似心跳來維持 長鏈接或狀態的分佈式場景而言這都是災難,這也是Azul的Zing JVM相比而言最大的優點(可實現不暫停的狀況下完成Compact,解決碎片問題)。 目前對於這樣的現象咱們惟一的解決辦法都是選擇在低峯期主動觸發Full GC(執行jmap -histo:live [pid])來避免碎片問題,但這顯然是一個很齷蹉的辦法(由於一樣會對心跳或維持狀態的分佈式場景形成影響)。 五、CMS GC的」不穩定「性 若是關注過我在以前的blog記錄的碰到的各類Java問題的文章(可在此查 看),就會發現碰到過不少各類CMS GC的詭異問題,儘管裏面碰到的大部分BUG目前均已在新版本的JVM修復,但誰也不知道是否是還有問題,畢竟CMS GC的實現是很是複雜的(由於要在儘量下降應用暫停時間的狀況下還保持對象引用的掃描不要出問題),而ParallelOldGC的實現相對是更簡單很 多的,所以穩定性相對高多了。
並且另一個不太好的消息是JVM Team的精力都已轉向G1GC和其餘的一些方面,CMS GC的投入已經不多了(這也正常,畢竟G1GC確實是方向)。 java

在大內存的狀況下,CMS GC絕對是不二的選擇,並且Java在面對內存愈來愈大的狀況下,必須採用這種大部分時候不暫停應用的方式,不然Java之後就很是悲催了,G1GC在 CMS GC的基礎上,有了不少的進步,尤爲是會作部分的Compact,但仍然碎片問題仍是存在的,哎… 微信

Java如今在大內存的狀況下還面臨的另外兩個大挑戰:
1. 分析內存的堆棧太麻煩,例如若是在大內存的狀況下出現OOM,那簡直就是杯具,想一想dump出一個幾十G的文件,而後還要分析,這得多長的時間呀,真心但願JDK在這方面能有更好的工具…
2. 對象結構不夠緊湊,致使在內存空間有很高要求的場景Java劣勢明顯,不過這也是新版本JDK會重點優化的地方。
至於在cpu cache miss等控制力度上不如C之類的語言,那是更沒辦法的,相比帶來的開發效率提高,也只能認了,畢竟如今多數場景都是工程性質和大規模人員的場景,所以開發效率、可維護性會更重要不少。 架構

推薦幾篇相關的文章:
1. A Generational Mostly-concurrent GC(CMS GC的理論論文)
2. The Pauseless GC Algorithm(能夠管窺下Zing是如何實現不暫停compact的)
3. Understanding CMS GC log 併發

最近在糾結一個問題,求有想法或建議的回下消息。
在一個打某種日誌的場景中,如何作到避免打日誌致使應用受影響,首先異步等是確定的,但因爲日誌量巨大,因此僅僅異步仍是會形成很大的IO壓力,但限流的 話到底怎麼限比較合理呢?(例如根據IOPS?但IOPS的話還得獲取硬件信息等,挺折騰,另外畢竟仍是想作到在能支撐的狀況下儘量不要丟棄這些日誌信 息),有此類場景經驗來給點建議吧。 框架

=============================
歡迎關注微信公衆號:hellojavacases less

關於此微信號:
分享Java問題排查的Case、Java業界的動態和新技術、Java的一些小知識點Test,以及和你們一塊兒討論一些Java問題或場景,這裏只有Java細節的分享,沒有大道理、大架構和大框架。 異步

公衆號上發佈的消息都存放在http://hellojava.info上。 分佈式

相關文章
相關標籤/搜索