1. 程式人生 > >JVM 經典垃圾收集器

JVM 經典垃圾收集器


> 本文部分摘自《深入理解 Java 虛擬機器第三版》
## 概述 如果說收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的實踐者。Java 虛擬機器規範中對垃圾收集器的實現做出規定,因此不同的廠商、不同版本的虛擬機器所包含的垃圾收集器各有不同。所謂經典就是在 JDK7 Update 4 以後,JDK11 釋出以前的在 OpenJDK HotSpot 虛擬機器所包含的全部可用的垃圾收集器。儘管這些經典垃圾收集器已算不上最先進的技術,但它們都經歷了千錘百煉,基本上都是可以放心使用的垃圾收集器。各款經典垃圾收集器之間的關係如圖所示,如果兩個收集器之間存在連線,就說明它們可以搭配使用: ![](https://img2020.cnblogs.com/blog/1759254/202012/1759254-20201225105926703-2085922468.png)
## Serial 收集器 Serial 收集器是最基礎、歷史最悠久的收集器,在 JDK3 以前是 HotSpot 虛擬機器新生代收集器的唯一選擇。這個收集器是一個單執行緒工作的收集器。這裡的單執行緒不僅僅是說明它只使用一條收集執行緒去完成垃圾收集工作,更強調的是它在進行垃圾收集時,必須暫停其他所有工作執行緒,直至收集結束,也即“Stop The World”。這項工作是由虛擬機器在後臺自動發起和完成的,使用者完全不可知,也不可控,顯然這令人難以接受 ![](https://img2020.cnblogs.com/blog/1759254/202012/1759254-20201225105942925-343767254.png) 但 Serial 收集器也有自己的優勢,那就是簡單與高效(與其他收集器的單執行緒相比)。對於記憶體有限的環境,它是所有收集器裡額外記憶體消耗最小的;對於單核處理器或處理器核心數較少的環境,Serial 收集器由於沒有執行緒互動的開銷,可以專心做垃圾收集,自然可以獲得最高的單執行緒收集效率。在使用者桌面應用場景以及近年來流行的部分微服務應用,分配給虛擬機器管理的記憶體一般不會太大,垃圾收集的停頓時間完全可以控制在毫秒級別,這點停頓時間對於使用者來說完全可以接受。所以,Serial 收集器對於執行在客戶端模式下的虛擬機器是一個不錯的選擇
## ParNew 收集器 ParNew 收集器實質上是 Serial 收集器的多執行緒並行版本,除了同時使用多條執行緒進行垃圾收集之外,其餘的行為包括 Serial 收集器可用的所有控制引數、收集演算法、Stop The World、物件分配規則、回收策略等都與 Serial 收集器完全一致,在實現上這兩種收集器也共用了許多程式碼 ![](https://img2020.cnblogs.com/blog/1759254/202012/1759254-20201225105957324-1371356689.png) ParNew 收集器除了支援多執行緒並行收集外,其餘與 Serial 收集器並無太多創新,但它卻是不少執行在服務端的 HotSpot 虛擬機器,尤其是 JDK7 之前的遺留系統首選的新生代收集器,其中最重要的原因是:除了 Serial 收集器,目前只有它能與 CMS 收集器配合工作。CMS 收集器是 HotSpot 虛擬機器中第一款真正意義上支援併發的垃圾收集器,它首次實現了讓垃圾收集器執行緒與使用者執行緒(基本上)同時工作。不過由於 JDK9 以後 CMS 逐漸被 G1 所代替,ParNew 也漸漸退出了歷史舞臺 ParNew 收集器由於存線上程互動開銷,當處於單核心處理器環境中時並不會有比 Serial 收集器更好的效果。不過,隨著可以被使用的處理器核心數的增加,ParNew 對於垃圾收集時系統資源的高效利用還是很有好處的
## Parallel Scavenge 收集器 Parallel Scavenge 收集器也是一款新生代收集器,它同樣是基於標記 - 複製演算法實現的收集器,也是能夠並行收集的多執行緒收集器。不同於 CMS 等收集器的關注點是儘可能地縮短垃圾收集時使用者執行緒的停頓時間,Parallel Scavenge 收集器的目標是達到一個控制的吞吐量。所謂吞吐量就是處理器用於執行使用者程式碼的時間與處理器總消耗時間的比值,即:吞吐量 = 執行使用者程式碼時間 / (執行使用者程式碼時間 + 執行垃圾收集時間) 停頓時間越短,越適合需要與使用者互動或需要保證服務響應質量的程式,提升使用者體驗;而高吞吐量則可以最高效率低利用處理器資源,儘快完成程式的運算任務。Parallel Scavenge 收集器提供了兩個引數用於精確控制吞吐量,分別是控制最大垃圾收集停頓時間的 -XX:MaxGCPauseMillis 引數以及直接設定吞吐量大小的 -XX:GCTimeRatio 引數 除上述兩個引數,Parallel Scavenge 收集器還有一個引數 -XX:UseAdaptiveSizePolicy,這是一個開關引數,當這個引數被啟用後,就不需要人工指定新生代的大小(-Xmn)、Eden 與 Survivor 區的比例(-XX:SurvivorRatio)、晉升老年代物件大小(-XX:PretenureSizeThreshold)等細節引數了,虛擬機器會根據當前系統的執行情況收集效能監控資訊,動態調整這些引數以提供最合適的停頓時間或最大1吞吐量,這種調節方式稱為垃圾收集的自適應調節策略
## Serial Old 收集器 Serial Old 收集器是 Serial 收集器的老年代版本,同樣是一個單執行緒收集器,使用標記 - 整理演算法。這個收集器的主要供客戶端模式下的 HotSpot 虛擬機器使用。如果用在服務端模式,可能有兩種用途:一種是在 JDK5 及之前的版本中與 Parallel Scavenge 收集器搭配使用,另一種就是作為 CMS 收集器發生失敗時的後備預案。Serial Old 收集器的工作過程如圖所示: ![](https://img2020.cnblogs.com/blog/1759254/202012/1759254-20201225110014402-69650751.png)
## Parallel Old 收集器 Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支援多執行緒併發收集,基於標記 - 整理演算法實現。這個收集器直到 JDK6 時才開始提供,在此之前,新生代的 Parallel Scavenge 收集器一直處於相當尷尬的狀態,因為如果新生代選擇了 Parallel Scavenge 收集器,那麼老年代除了 Serial Old 收集器以外就別無選擇,效率不高。直到 Parallel Old 收集器的出現,吞吐量優先收集器終於有了比較名副其實的搭配組合。Parallel Old 收集器的工作過程如圖所示: ![](https://img2020.cnblogs.com/blog/1759254/202012/1759254-20201225110026831-233223968.png