Hotspot垃圾回收器
Hotspot垃圾回收器
HotSpot虛擬機提供了多種垃圾收集器,每種收集器都有各自的特點,沒有最好的垃圾收集器,只有最適合的垃圾收集器。我們可以根據自己實際的應用需求選擇最適合的垃圾收集器。根據新生代和老年代各自的特點,我們應該分別為它們選擇不同的收集器,以提升垃圾回收效率。
一、新生代垃圾收集器
1. Serial垃圾收集器
-
單線程
只開啟一條GC線程進行垃圾回收,並且在垃圾回收過程中停止一切用戶線程,從而用戶的請求或圖形化界面會出現卡頓。 -
適合客戶端應用
一般客戶端應用所需內存較小,不會創建太多的對象,而且堆內存不大,因此垃圾回收時間比較短,即使在這段時間停止一切用戶線程,用戶也不會感受到明顯的停頓,因此本垃圾收集器適合客戶端應用。 -
簡單高效
由於Serial收集器只有一條GC線程,因此避免了線程切換的開銷,從而簡單高效。 -
采用“復制”算法
2. ParNew垃圾收集器
ParNew是Serial的多線程版本。
1. 多線程並行執行
ParNew由多條GC線程並行地進行垃圾清理。但清理過程仍然需要停止一切用戶線程。但由於有多條GC線程同時清理,清理速度比Serial有一定的提升。
-
適合多CPU的服務器環境
由於使用了多線程,因此適合CPU較多的服務器環境。 -
與Serial性能對比
ParNew和Serial唯一的區別就是使用了多線程進行垃圾回收,在多CPU的環境下性能比Serial會有一定程度的提升;但線程切換需要額外的開銷,因此在單CPU環境中表現不如Serial。 -
采用“復制”算法
- 追求“降低停頓時間”
和Serial相比,ParNew使用多線程的目的就是縮短垃圾收集時間,從而減少用戶線程被停頓的時間。
3. Parallel Scavenge垃圾收集器
Parallel Scavenge和ParNew一樣都是多線程、新生代收集器,都使用“復制”算法進行垃圾回收。但它們有個巨大的不同點:ParNew收集器追求降低用戶線程的停頓時間,因此適合交互式應用;而Parallel Scavenge追求CPU吞吐量,能夠在較短的時間內完成指定任務,因此適合沒有交互的後臺計算。
-
什麽是“吞吐量”?
吞吐量是指用戶線程運行時間占CPU總時間的比例。
CPU總時間包括:用戶線程運行時間 和 GC線程運行的時間。
因此,吞吐量越高表示用戶線程運行時間越長,從而用戶線程能夠被快速處理完。 -
降低停頓時間的兩種方式
1.在多CPU環境中使用多條GC線程,從而垃圾回收的時間減少,從而用戶線程停頓的時間也減少;
2.實現GC線程與用戶線程並發執行。所謂並發,就是用戶線程與GC線程交替執行,從而每次停頓的時間會減少,用戶感受到的停頓感降低,但線程之間不斷切換意味著需要額外的開銷,從而垃圾回收和用戶線程的總時間將會延長。 -
Parallel Scavenge提供的參數
-
設置“吞吐量”
通過參數-XX:GCTimeRadio設置垃圾回收時間占總CPU時間的百分比。 -
設置“停頓時間”
通過參數-XX:MaxGCPauseMillis設置垃圾處理過程最久停頓時間。Parallel Scavenge會根據這個值的大小確定新生代的大小。如果這個值越小,新生代就會越小,從而收集器就能以較短的時間進行一次回收。但新生代變小後,回收的頻率就會提高,因此要合理控制這個值。 -
啟用自適應調節策略
通過命令-XX:+UseAdaptiveSizePolicy就能開啟自適應策略。我們只要設置好堆的大小和MaxGCPauseMillis或GCTimeRadio,收集器會自動調整新生代的大小、Eden和Survior的比例、對象進入老年代的年齡,以最大程度上接近我們設置的MaxGCPauseMillis或GCTimeRadio。
-
二、老年代垃圾收集器
1. Serial Old垃圾收集器
Serial Old收集器是Serial的老年代版本,它們都是單線程收集器,也就是垃圾收集時只啟動一條GC線程,因此都適合客戶端應用。
它們唯一的區別就是Serial Old工作在老年代,使用“標記-整理”算法;而Serial工作在新生代,使用“復制”算法。
2. Parallel Old垃圾收集器
Parallel Old收集器是Parallel Scavenge的老年代版本,一般它們搭配使用,追求CPU吞吐量。
它們在垃圾收集時都是由多條GC線程並行執行,並停止一切用戶線程。因此,由於在垃圾清理過程中沒有使垃圾收集和用戶線程並行執行,因此它們是追求吞吐量的垃圾收集器。
3. CMS垃圾收集器
CMS收集器是一款追求停頓時間的老年代收集器,它在垃圾收集時使得用戶線程和GC線程並行執行,因此在垃圾收集過程中用戶也不會感受到明顯的卡頓。但用戶線程和GC線程之間不停地切換會有額外的開銷,因此垃圾回收總時間就會被延長。
垃圾回收過程
- 初始標記
停止一切用戶線程,僅使用一條初始標記線程對所有與GC ROOTS直接關聯的對象進行標記。速度很快。 - 並發標記
使用多條並發標記線程並行執行,並與用戶線程並發執行。此過程進行可達性分析,標記出所有廢棄的對象。速度很慢。 - 重新標記
停止一切用戶線程,並使用多條重新標記線程並行執行,將剛才並發標記過程中新出現的廢棄對象標記出來。這個過程的運行時間介於初始標記和並發標記之間。 - 並發清除
只使用一條並發清除線程,和用戶線程們並發執行,清除剛才標記的對象。這個過程非常耗時。
CMS的缺點
- 吞吐量低
由於CMS在垃圾收集過程使用用戶線程和GC線程並行執行,從而線程切換會有額外開銷,因此CPU吞吐量就不如在垃圾收集過程中停止一切用戶線程的方式來的高。 - 無法處理浮動垃圾,導致頻繁Full GC
由於垃圾清除過程中,用戶線程和GC線程並發執行,也就是用戶線程仍在執行,那麽在執行過程中會產生垃圾,這些垃圾稱為“浮動垃圾”。
如果CMS在垃圾清理過程中,用戶線程需要在老年代中分配內存時發現空間不足時,就需要再次發起Full GC,而此時CMS正在進行清除工作,因此此時只能由Serial Old臨時對老年代進行一次Full GC。 - 使用“標記-清除”算法產生碎片空間
由於CMS使用了“標記-清除”算法, 因此清除之後會產生大量的碎片空間,不利於空間利用率。不過CMS提供了應對策略:
- 開啟-XX:+UseCMSCompactAtFullCollection
開啟該參數後,每次FullGC完成後都會進行一次內存壓縮整理,將零散在各處的對象整理到一塊兒。但每次都整理效率不高,因此提供了以下參數。 - 設置參數-XX:CMSFullGCsBeforeCompaction
本參數告訴CMS,經過了N次Full GC過後再進行一次內存整理。
- 開啟-XX:+UseCMSCompactAtFullCollection
三、通用垃圾收集器——G1垃圾收集器
G1是目前最牛逼的垃圾收集器。
G1的特點
- 追求停頓時間
- 多線程GC
- 面向服務端應用
- 標記-整理和復制算法合並
不會產生碎片內存。 - 可對整個堆進行垃圾回收
- 可預測停頓時間
G1的內存模型
G1垃圾收集器沒有新生代和老年代的概念了,而是將堆劃分為一塊塊獨立的Region。當要進行垃圾收集時,首先估計每個Region中的垃圾數量,每次都從垃圾回收價值最大的Region開始回收,因此可以獲得最大的回收效率。
Remembered Set
一個對象和它內部所引用的對象可能不在同一個Region中,那麽當垃圾回收時,是否需要掃描整個堆內存才能完整地進行一次可達性分析?
當然不是,每個Region都有一個Remembered Set,用於記錄本區域中所有對象引用的對象所在的區域,從而在進行可達性分析時,只要在GC ROOTs中再加上Remembered Set即可防止對所有堆內存的遍歷。
G1垃圾收集過程
- 初始標記
標記與GC ROOTS直接關聯的對象,停止所有用戶線程,只啟動一條初始標記線程,這個過程很快。 - 並發標記
進行全面的可達性分析,開啟一條並發標記線程與用戶線程並行執行。這個過程比較長。 - 最終標記
標記出並發標記過程中用戶線程新產生的垃圾。停止所有用戶線程,並使用多條最終標記線程並行執行。 - 篩選回收
回收廢棄的對象。此時也需要停止一切用戶線程,並使用多條篩選回收線程並行執行。
Hotspot垃圾回收器