面試官:談談你對JVM垃圾收集器的瞭解

本文默認JVM爲HotSpot。以前已經介紹過常見的垃圾收集算法,此次再來講說JVM具體實現了這些算法的垃圾收集器。算法

下圖爲HotSpot虛擬機有的垃圾收集器,有連線關係的表示能夠搭配使用,反之不能。 服務器

HotSpot虛擬機垃圾收集器
每種垃圾收集器都有各自的特性,咱們須要根據不一樣的場景來使用不一樣的垃圾收集器。

新生代垃圾收集器

Serial

此垃圾收集器年代久遠,用於新生代的垃圾收集,採用複製算法。是單線程的垃圾收集器也就是無論你的服務器有多少CPU,反正它就用其中的一個CPU啓動一個線程去處理垃圾回收,而且中止全部工做線程等待它回收完成。因此它在收集時會STW(stop the world)。能與其搭配的老年代收集器是CMS與Serial Old。 多線程

Serial與Serial Old搭配
單線程的好處就在於它簡單,沒有上下文線程切換的開銷。多用於桌面應用中,也就是適用於client模式。由於桌面應用通常佔用的內存不大,內存不大表明須要處理的垃圾很少,因此即便單線程也能處理很快,因此感覺不到STW。是client模式下默認的新生代垃圾收集器。

ParNew

此垃圾收集器能夠說是Serial的多線程版本,它和Serial的差異就在於複製的時候是多線程的。 併發

ParNew與Serial Old搭配
它主要是能利用多CPU,提高複製的速度,減小STW的時間。可是在單CPU狀況下不要使用它,由於線程切換有開銷,性能不必定會比Serial好,固然若是CPU數不少的話那性能確定是比Serial好的。因此在Server模式下能夠用它來做爲新生代垃圾處理器。能與其搭配的老年代收集器是CMS與Serial Old。

Parallel Scavenge

Scavenge是撿破爛的意思...恩並行撿破爛說的是好像沒錯,用的也是複製算法。那不是已經在ParNew了嗎,怎麼還來個並行的。它和ParNew主要有兩個不一樣點框架

一、Parallel Scavenge的關注點在於可控制的吞吐量,吞吐量=運行用戶代碼的時間/(運行用戶代碼的時間+GC時間)。就是說它的重點不在於想縮短每次GC的時間,而在於控制虛擬機運行一段時間中,所花費在GC上的總時間。好比程序運行了100分鐘,其間垃圾收集了1分鐘,那吞吐量就是99%。post

二、Parallel Scavenge能自適應調節新生代中配置的參數,例如Eden和survivor比例等。其實就是由於它能自適應,因此才能可控制吞吐量,它根據實際狀況動態調整這些參數來達到要求的吞吐量。性能

此收集器也提供了「-XX:MaxGCPauseMillis」控制垃圾收集最大停頓時間(容許值大於0),「-XX:GCTimeRatio」吞吐量(1-99)。 看到「-XX:MaxGCPauseMillis」,別覺得咱們想設置多少就多少,收集器只能儘量的保證而已。並且說白了能若是想提升新生代GC的速度,那就是減小新生代的內存空間,內存空間少垃圾確定少處理起來確定快。可是空間少是否是更快的容易滿啊,因此所需的GC次數確定會增多,那吞吐量也會降低。線程

好比說一個程序如今跑在服務器上,假設每次新生代GC時間是100毫秒,每10秒鐘一次新生代GC,那一分鐘花費在GC上的時間就是600毫秒。那我想每次花在GC時間更少好比60毫秒,那就減小新生代內存空間,可是這樣每5秒鐘一次GC,那一分鐘花費在GC上的時間就是720毫秒。3d

對應使用的場景就是若是你的服務是計算類的,默默在後臺計算,和用戶交互不多,因此你確定想的是吞吐量大,也就是總的GC時間短,能充分的用了CPU來計算,這個時候就適合用Parallel Scavenge。cdn

那若是你的程序是交互類的,你的要求確定就是STW的時間越短越好,能快速響應客戶的請求。Parallel Scavenge也行,可是它不能和CMS聯合使用呀!由於Parallel Scavenge沒有使用本來HotSpot中和其它GC通用的那個GC框架,而是新框架。因此默認和CMS搭配的就是ParNew。

Parallel Scavenge與Parallel Old搭配

老年代垃圾收集器

Serial Old

它是Serial 收集器的老年代版本,是單線程收集,採用的是標記-整理算法。主要用於client模式和CMS的後備收集器。除了G1,上面說的幾個新生代收集器均可以與它搭配使用。圖請參考上面Serial。

Paraller Old

它是Parallel Scavenge的老年代版本,是多線程收集,採用的是標記-整理算法。它只能和Parallel Scavenge搭配。它的出現打破了Parallel Scavenge尷尬的地位,由於以前Parallel Scavenge只能和Serial Old配合,人家新生代都多線程跑了,奈何老年代只有單線程,拖累它了。圖請參考上面Parallel Scavenge。

CMS

CMS(Concurrent Mark Sweep),從名字能夠看出它採用的是標記-清除算法。它致力於減小STW的時間,讓垃圾收集時同時用戶線程也能並行着。在目前的Server主流垃圾收集器。

CMS
它的垃圾收集步驟分爲如下4步:

一、初始標記(會STW)

二、併發標記

三、從新標記(會STW)

四、併發清理

初始標記就是僅標記GC Roots直接關聯的對象,不繼續深刻標記,致力於減小STW時間。併發標記就是深刻標記遍歷後面全部關聯對象。從新標記就是修正因併發標記階段而發生變更了的對象標記會STW。而後就是併發清理垃圾。

因此CMS把所需消耗時間最長的深刻標記階段和清理階段與用戶線程並行。大大減小了STW所需的時間。

可是它有如下3個缺點:

一、併發階段會與工做線程爭搶CPU資源

二、空間碎片問題,由於採起的是標記-清除算法因此會產生空間碎片。爲何解決這個問題CMS提供了"-XX:+UseCMSCompactAtFullCollection"(默認開啓),用於當CMS頂不住須要進行FullGC時整理空間碎片,可是整理的過程是用戶線程是得中止工做的,因此停頓的時間會變長。

三、浮動垃圾問題。由於在併發清理的時候容許用戶線程繼續執行,而執行就可能產生新的垃圾進入老年代,因此須要預留一部分空間給這些浮動垃圾,而當這些浮動垃圾過多在CMS運行期間爆了,那CMS就會出現「Concurrent Mode Failure」,這是時候就得後備的Serial Old上來從新進行老年代的垃圾收集,因此停頓的時間就更長了。

G1

此垃圾收集器不須要和別人配合,本身處理新生代和老年代。在jdk9中G1變爲Server模式默認的垃圾收集器。它的發明就是爲了替代CMS。

G1(Garbage-First)從總體來看是基於標記-整理的算法,從局部來看是基於複製算法。它和CMS同樣能夠和用戶進程並行。相對於CMS 它的優勢是首先它能創建可預測的停頓時間模型,能在一個規定的時間段內指定垃圾收集的時間不超過限制的毫秒數,而且它將Java堆分爲多個大小相等的獨立區域,也就是Region。雖然它還保留着分代的概念,可是新生代和老年代不是物理隔離了。 它的清理區間再也不是整個新生代或者老年代,而是以區域爲劃分,不會產生空間碎片

G1會維護一個優先列表,根據跟蹤各個region回收所能產生的空間大小和時間來標定優先級,優先回收優先級最大的Region。這就等於每次的回收目標更加精確化,提升回收的效率 G1的收集步驟可分爲:

一、初始標記

二、併發標記

三、最終標記

四、篩選回收

G1

初始標記和CMS同樣先標記GC Roots直接關聯對象,而後併發深刻標記,遍歷關聯對象。最終標記和CMS從新標記一個概念,篩選回收也就是篩選下決定回收哪一個Region價值更大。


若是有錯歡迎指正!我的公衆號:yes的練級攻略

相關文章
相關標籤/搜索