面試Java崗!說一下 jvm 有哪些垃圾回收器?
在JDK1.3之前,單執行緒回收器是唯一的選擇。它的單執行緒意義不僅僅是說它只會使用一個CPU或一個手機執行緒去完成垃圾收集工作。而且它進行垃圾回收的時候,必須暫停其它所有的工作執行緒(Stop The World,STW),直到它收集完成。它適合Client模式的應用,在單CPU環境下,它效率高效,由於沒有執行緒互動的開銷,專心垃圾收集自然可以獲得最高的單執行緒效率。
序列的垃圾收集器有兩種,Serial和Serial Old,一般兩者搭配使用。
新生代採用Serial,是利用複製演算法;老年代使用Serial Old採用標記-整理演算法。Client應用或者命令列程式可以通過
-XX:+UseSerialGC開啟序列垃圾回收器。
二、並行垃圾回收器
並行垃圾回收器是通過多執行緒進行垃圾收集的。也會暫停其它所有的工作執行緒(Stop The World,STW)。適合Server模式以及多CPU環境。一般會和JDK1.5之後出現的CMS搭配使用。並行的垃圾回收器有以下幾種:
ParNew:Serial收集器的多執行緒版本,預設開啟的收集執行緒數和CPU數量一樣,執行數量可以通過修改ParallelGCThreads設定。用於新生代手機,複製演算法。用-XX:+UseParNewGC,和Serial Old收集器組合進行記憶體回收。
?Parallel Scavenge: 關注吞吐量,吞吐量優先,吞吐量=程式碼執行時間/(程式碼執行時間+垃圾收集時間),也就是高效率利用CPU時間,儘快完成程式的運算任務可以升值最大停頓時間MaxGCPauseMillis以及,吞吐量大小GCTimeRatio。如果設定了-XX:+UseAdaptiveSizePolicy引數,則隨著GC,會動態調整新生代的大小,Eden,Survivor比例等,以提供最合適的停頓時間或者最大的吞吐量。用於新生代收集,複製演算法。通過-XX:+UseParallelGC引數,Server模式下預設提供了其和SerialOld進行搭配的分代收集方式。
Parllel Old:Parallel Scavenge的老年代版本。JDK 1.6開始提供的。在此之前Parallel Scavenge的地位也很尷尬,而有了Parllel Old之後,通過-XX:+UseParallelOldGC引數使用Parallel Scavenge + Parallel Old器組合進行記憶體回收。
三、CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲得最短回收停頓時間為目標的收集器。從名字就能知道它是標記-清除演算法的。但是它比一般的標記-清除演算法要複雜一些,分為以下4個階段:
-
初始標記:標記一下GC Roots能直接關聯到的物件,會"Stop The World"。
-
併發標記:GC Roots Tracing,可以和使用者執行緒併發執行。
-
重新標記:標記期間產生的物件存活的再次判斷,修正對這些物件的標記,執行時間相對併發標記短,會“Stop The World”。
-
併發清除:清除物件,可以和使用者執行緒併發執行。
由於垃圾回收執行緒可以和使用者執行緒同時執行,也就是說它是併發的,那麼它會對CPU的資源非常敏感,CMS預設啟動的回收執行緒數是(CPU數量+3)/ 4,當CPU<4個時,併發回收是垃圾收集執行緒就不會少於25%,而且隨著CPU減少而增加,這樣會影響使用者執行緒的執行。而且由於它是基於標記-清除演算法的,那麼就無法避免空間碎片的產生。CMS收集器無法處理浮動垃圾(Floating Garbage),可能出現“Concurrent Mode Failure”失敗而導致另一次Full GC的產生。
? ? ? ? 所謂浮動垃圾,在CMS併發清理階段使用者執行緒還在執行著,伴隨程式執行自然還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在當次收集中處理掉它們,只能留待下一次GC時再清理掉。
四、G1垃圾收集器
1、G1垃圾收集器
把G1單獨拿出來的原因是其比較複雜,在JDK 1.7確立是專案目標,在JDK 7u2版本之後釋出,並在JDK 9中成為了預設的垃圾回收器。通過“-XX:+UseG1GC”啟動引數即可指定使用G1 GC。
G1從整體看還是基於標記-清除演算法的,但是區域性上是基於複製演算法的。這樣就意味者它空間整合做的比較好,因為不會產生空間碎片。G1還是併發與並行的,它能夠充分利用多CPU、多核的硬體環境來縮短“stop the world”的時間。G1還是分代收集的,但是G1不再像上文所述的垃圾收集器,需要分代配合不同的垃圾收集器,因為G1中的垃圾收集區域是“分割槽”(Region)的。G1的分代收集和以上垃圾收集器不同的就是除了有年輕代的ygc,全堆掃描的full?GC外,還有包含所有年輕代以及部分老年代Region的Mixed?GC。G1還可預測停頓,通過調整引數,制定垃圾收集的最大停頓時間。
G1收集器的運作大致可以分為以下步驟:初始標記、併發標記、最終標記、篩選回收。其中初始標記階段僅僅只是標記一下GC Roots能直接關聯到的物件,並且修改TAMS(Next Top at Mark Set)的值,讓下一個階段使用者程式併發執行時,能在正確可用的Region中建立新物件,這個階段需要STW,但耗時很短。併發標記階段是從GC Roots開始對堆中物件進行可達性分析,找到存活的物件,這階段耗時較長,但是可以和使用者執行緒併發執行。最終標記階段則是為了修正在併發標記期間因使用者程式繼續執行而導致標記產生變化的那一部分標記記錄,虛擬機器將這段時間物件變化記錄線上程Remembered Set Logs裡面,最終標記需要把Remembered Set Logs的資料合併到Remembered Sets中,這階段需要暫停執行緒,但是可並行執行。最後的篩選回收階段首先對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間來確定回收計劃。G1收集器執行示意圖如下圖所示。
2、G1分割槽的概念
G1的堆區在分代的基礎上,引入分割槽的概念。G1將堆分成了若干Region,以下和”分割槽”代表同一概念。(這些分割槽不要求是連續的記憶體空間)Region的大小可以通過G1HeapRegionSize引數進行設定,其必須是2的冪,範圍允許為1Mb到32Mb。 JVM的會基於堆記憶體的初始值和最大值的平均數計算分割槽的尺寸,平均的堆尺寸會分出約2000個Region。分割槽大小一旦設定,則啟動之後不會再變化。如下圖簡單畫了下G1分割槽模型。
Eden regions(年輕代-Eden區)
Survivor regions(年輕代-Survivor區)
Old regions(老年代)
Humongous regions(巨型物件區域)
Free regions(未分配區域,也會叫做可用分割槽)-上圖中空白的區域
G1中的巨型物件是指,佔用了Region容量的50%以上的一個物件。Humongous區,就專門用來儲存巨型物件。如果一個H區裝不下一個巨型物件,則會通過連續的若干H分割槽來儲存。因為巨型物件的轉移會影響GC效率,所以併發標記階段發現巨型物件不再存活時,會將其直接回收。ygc也會在某些情況下對巨型物件進行回收。
分割槽可以有效利用記憶體空間,因為收集整體是使用“標記-整理”,Region之間基於“複製”演算法,GC後會將存活物件複製到可用分割槽(未分配的分割槽),所以不會產生空間碎片。
3、G1 GC的分類和過程
JDK10 之前的G1中的GC只有Young?GC,Mixed?GC。Full?GC處理會交給單執行緒的Serial Old垃圾收集器。
技術學習總結
學習技術一定要制定一個明確的學習路線,這樣才能高效的學習,不必要做無效功,既浪費時間又得不到什麼效率,大家不妨按照我這份路線來學習。
最後面試分享
大家不妨直接在牛客和力扣上多刷題,同時,我也拿了一些面試題跟大家分享,也是從一些大佬那裡獲得的,大家不妨多刷刷題,為金九銀十衝一波!
最後,若需要完整pdf版,可以點贊本文後點選這裡免費領取