经典面试题 | 讲一下垃圾回收器都有哪些?
垃圾回收器有哪些?是一個高頻的面試題,那本文就詳細來解答這個問題。
因為不同的廠商(IBM、Oracle),實現的垃圾回收器各不相同,而本文要討論的是 Oracle 的 HotSpot 虛擬機所使用的垃圾回收器。
常用垃圾回收器,如下圖所示:
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
其中相互連線的垃圾回收器,表示可以相互搭配使用。
新生代 And 老生代
目前常用的商用垃圾收集器都使用的是分代垃圾回收方式。
分代垃圾回收器把內存分為:新生代(Young Generation)和老生代(Tenured Generation),如下圖所示:
(圖片來自fancydeepin)
默認情況下,新生代和老生代的內存比例是 1:2,該值可以通過 -XX:NewRatio 來設定。
新生代(Young Generation)
程序中的大部分對象都符合“朝生夕死”的特性,所以絕大數新創建的對象都會存放在新生代,除非是大對象會直接進入老生代。新生代采用的是復制算法,這樣可以更高效的回收內存空間。
新生代有細分為:Eden、Form Survivor、To Survivor 三個區域,默認的比例是 8:1:1,可以通過 -XX:SurvivorRatio 來設定。
新生代垃圾回收的執行過程:
1、Eden 區 + From Survivor 區存活著的對象復制到 To Survivor 區;
2、清空 Eden 和 From Survivor 分區;
3、From Survivor 和 To Survivor 分區交換(From 變 To,To 變 From)。
老生代(Tenured Generation)
老生代垃圾回收的頻率比新生代低,存放的主要對象是:
1、新生代對象經過 N 次 GC 晉升到老年代。
可以通過設置 -XX:MaxTenuringThreshold=5 來設置,默認值是 15 次。
2、大對象直接存儲到老生代。
所謂的“大對象”指的是需要連續存儲空間的對象,比如:數組。
當大對象在新生代存儲不下的時候,就需要分配擔保機制,把當前新生代的所有對象復制到老年代中,因為分配擔保機制需要涉及大量的復制,會導致性能問題,所有最好的方案是直接把大對象存儲到老生代中。
通過參數 -xx:PretrnureSizeThreshold 來設定大對象的值。
注意:該參數只有 Serial 和 ParNew 垃圾回收器有效。
Serial
Serial 最早的垃圾回收器,JDK 1.3.1 之前新生代唯一的垃圾回收器,使用的是單線程串行回收方式,在單 CPU 環境下性能較好,因為單線程執行不存在線程切換。
線程類型: 單線程
使用算法: 復制算法
指定收集器: -XX:+UseSerialGC
Serial Old
Serial 收集器的老年代版本,同樣也是單線程的。它有一個實用的用途作為CMS收集器的備選預案,后面介紹CMS的時候會詳細介紹。
線程類型: 單線程
使用算法: 標記-整理
指定收集器: -XX:+UseSerialGC
ParNew
ParNew 其實就是 Serial 的多線程版本,可以和 Serial 共用很多控制參數,比如:-XX:SurvivorRatio , ParNew 可以和 CMS 配合使用。
(注:圖片來源于零壹技術棧)
線程類型: 多線程
使用算法: 復制
指定收集器: -XX:+UseParNewGC
Parallel Scavenge
Parallel 和 ParNew 收集器類似,也是多線程的,但 Parallel 是吞吐量優先的收集器,GC停頓時間的縮短是以吞吐量為代價的,比如收集 100MB 的內存,需要 10S 的時間,CMS 則會縮短為 7S 收集 50 MB 的內存,這樣停頓的時間確實縮少了,但收集的頻率變大了,吞吐量就變小了。
線程類型: 多線程
使用算法: 復制
指定收集器: -XX:+UseParallelGC
Parallel Old
Parallel Old 是 Parallel 的老生代版本,同樣是吞吐量優先的收集器。
線程類型: 多線程
使用算法: 標記-整理
指定收集器: -XX:+UseParallelOldGC
CMS
CMS(Concurrent Mark Sweep)一種以獲得最短停頓時間為目標的收集器,非常適用B/S系統。
使用 Serial Old 整理內存。
CMS 運行過程:
(注:圖片來源于零壹技術棧)
1、初始標記
標記 GC Roots 直接關聯的對象,需要 Stop The World 。
2、并發標記
從 GC Roots 開始對堆進行可達性分析,找出活對象。
3、重新標記
重新標記階段為了修正并發期間由于用戶進行運作導致的標記變動的那一部分對象的標記記錄。這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發標記的時間短,也需要 Stop The World 。
4、并發清除
除垃圾對象。
CMS 缺點:
1、對 CPU 資源要求敏感。
CMS 回收器過分依賴于多線程環境,默認情況下,開啟的線程數為(CPU 的數量 + 3)/ 4,當 CPU 數量少于 4 個時,CMS 對用戶本身的操作的影響將會很大,因為要分出一半的運算能力去執行回收器線程。
2、CMS無法清除浮動垃圾。
浮動垃圾指的是CMS清除垃圾的時候,還有用戶線程產生新的垃圾,這部分未被標記的垃圾叫做“浮動垃圾”,只能在下次 GC 的時候進行清除。
3、CMS 垃圾回收會產生大量空間碎片。
CMS 使用的是標記-清除算法,所有在垃圾回收的時候回產生大量的空間碎片。
注意:CMS 收集器中,當老生代中的內存使用超過一定的比例時,系統將會進行垃圾回收;當剩余內存不能滿足程序運行要求時,系統將會出現 Concurrent Mode Failure,臨時采用 Serial Old 算法進行清除,此時的性能將會降低。
線程類型: 多線程
使用算法: 標記-清除
指定收集器: -XX:+UseConcMarkSweepGC
G1
G1 GC 這是一種兼顧吞吐量和停頓時間的 GC 實現,是 JDK 9 以后的默認 GC 選項。G1 可以直觀的設定停頓時間的目標,相比于 CMS GC,G1 未必能做到 CMS 在最好情況下的延時停頓,但是最差情況要好很多。
G1 GC 仍然存在著年代的概念,但是其內存結構并不是簡單的條帶式劃分,而是類似棋盤的一個個 region。Region 之間是復制算法,但整體上實際可看作是標記 - 整理(Mark-Compact)算法,可以有效地避免內存碎片,尤其是當 Java 堆非常大的時候,G1 的優勢更加明顯。
G1 吞吐量和停頓表現都非常不錯,并且仍然在不斷地完善,與此同時 CMS 已經在 JDK 9 中被標記為廢棄(deprecated),所以 G1 GC 值得深入掌握。
G1 運行過程:
1、初始標記
標記 GC Roots 直接關聯的對象,需要 Stop The World 。
2、并發標記
從 GC Roots 開始對堆進行可達性分析,找出活對象。
3、重新標記
重新標記階段為了修正并發期間由于用戶進行運作導致的標記變動的那一部分對象的標記記錄。這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發標記的時間短,也需要 Stop The World 。
4、篩選回收
首先對各個 Region 的回收價值和成本進行排序,根據用戶所期望的 GC 停頓時間來制定回收計劃。這個階段可以與用戶程序一起并發執行,但是因為只回收一部分 Region,時間是用戶可控制的。
線程類型: 多線程
使用算法: 復制、標記-整理
指定收集器: -XX:+UseG1GC(JDK 7u4 版本后可用)
最后:本文歡迎任何形式的轉載,如需開通白名單,請在評論區留下公眾號ID即可。
參考
【End】
查看更多面試題內容,請訪問《Java最常見200+面試題全解析》,它包含的模塊有:
Java、JVM?最常見面試題解析
Spring、Spring?MVC、MyBatis、Hibernate?面試題解析
MySQL、Redis?面試題解析
RabbitMQ、Kafka、Zookeeper?面試解析
微服務?Spring?Boot、Spring?Cloud?面試解析
掃描下面二維碼付費閱讀
關注下方二維碼,訂閱更多精彩內容。
轉發朋友圈,是對我最大的支持。
總結
以上是生活随笔為你收集整理的经典面试题 | 讲一下垃圾回收器都有哪些?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试系列第1篇:常见面试题和面试套路有哪
- 下一篇: 面试官 | count(1)、count