1. 程式人生 > >Hotspot垃圾回收器

Hotspot垃圾回收器

ref collect serial 使用 區域 rec data 是否 而是

Hotspot垃圾回收器


HotSpot虛擬機提供了多種垃圾收集器,每種收集器都有各自的特點,沒有最好的垃圾收集器,只有最適合的垃圾收集器。我們可以根據自己實際的應用需求選擇最適合的垃圾收集器。根據新生代和老年代各自的特點,我們應該分別為它們選擇不同的收集器,以提升垃圾回收效率。

一、新生代垃圾收集器

1. Serial垃圾收集器

  1. 單線程
    只開啟一條GC線程進行垃圾回收,並且在垃圾回收過程中停止一切用戶線程,從而用戶的請求或圖形化界面會出現卡頓。

  2. 適合客戶端應用
    一般客戶端應用所需內存較小,不會創建太多的對象,而且堆內存不大,因此垃圾回收時間比較短,即使在這段時間停止一切用戶線程,用戶也不會感受到明顯的停頓,因此本垃圾收集器適合客戶端應用。

  3. 簡單高效
    由於Serial收集器只有一條GC線程,因此避免了線程切換的開銷,從而簡單高效。

  4. 采用“復制”算法

2. ParNew垃圾收集器

ParNew是Serial的多線程版本。
1. 多線程並行執行
ParNew由多條GC線程並行地進行垃圾清理。但清理過程仍然需要停止一切用戶線程。但由於有多條GC線程同時清理,清理速度比Serial有一定的提升。

  1. 適合多CPU的服務器環境
    由於使用了多線程,因此適合CPU較多的服務器環境。

  2. 與Serial性能對比
    ParNew和Serial唯一的區別就是使用了多線程進行垃圾回收,在多CPU的環境下性能比Serial會有一定程度的提升;但線程切換需要額外的開銷,因此在單CPU環境中表現不如Serial。

  3. 采用“復制”算法

  4. 追求“降低停頓時間”
    和Serial相比,ParNew使用多線程的目的就是縮短垃圾收集時間,從而減少用戶線程被停頓的時間。

3. Parallel Scavenge垃圾收集器

Parallel Scavenge和ParNew一樣都是多線程、新生代收集器,都使用“復制”算法進行垃圾回收。但它們有個巨大的不同點:ParNew收集器追求降低用戶線程的停頓時間,因此適合交互式應用;而Parallel Scavenge追求CPU吞吐量,能夠在較短的時間內完成指定任務,因此適合沒有交互的後臺計算。

  1. 什麽是“吞吐量”?
    吞吐量是指用戶線程運行時間占CPU總時間的比例。
    CPU總時間包括:用戶線程運行時間 和 GC線程運行的時間。
    因此,吞吐量越高表示用戶線程運行時間越長,從而用戶線程能夠被快速處理完。

  2. 降低停頓時間的兩種方式
    1.在多CPU環境中使用多條GC線程,從而垃圾回收的時間減少,從而用戶線程停頓的時間也減少;
    2.實現GC線程與用戶線程並發執行。所謂並發,就是用戶線程與GC線程交替執行,從而每次停頓的時間會減少,用戶感受到的停頓感降低,但線程之間不斷切換意味著需要額外的開銷,從而垃圾回收和用戶線程的總時間將會延長。

  3. 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線程之間不停地切換會有額外的開銷,因此垃圾回收總時間就會被延長。

垃圾回收過程

  1. 初始標記
    停止一切用戶線程,僅使用一條初始標記線程對所有與GC ROOTS直接關聯的對象進行標記。速度很快。
  2. 並發標記
    使用多條並發標記線程並行執行,並與用戶線程並發執行。此過程進行可達性分析,標記出所有廢棄的對象。速度很慢。
  3. 重新標記
    停止一切用戶線程,並使用多條重新標記線程並行執行,將剛才並發標記過程中新出現的廢棄對象標記出來。這個過程的運行時間介於初始標記和並發標記之間。
  4. 並發清除
    只使用一條並發清除線程,和用戶線程們並發執行,清除剛才標記的對象。這個過程非常耗時。

CMS的缺點

  1. 吞吐量低
    由於CMS在垃圾收集過程使用用戶線程和GC線程並行執行,從而線程切換會有額外開銷,因此CPU吞吐量就不如在垃圾收集過程中停止一切用戶線程的方式來的高。
  2. 無法處理浮動垃圾,導致頻繁Full GC
    由於垃圾清除過程中,用戶線程和GC線程並發執行,也就是用戶線程仍在執行,那麽在執行過程中會產生垃圾,這些垃圾稱為“浮動垃圾”。
    如果CMS在垃圾清理過程中,用戶線程需要在老年代中分配內存時發現空間不足時,就需要再次發起Full GC,而此時CMS正在進行清除工作,因此此時只能由Serial Old臨時對老年代進行一次Full GC。
  3. 使用“標記-清除”算法產生碎片空間
    由於CMS使用了“標記-清除”算法, 因此清除之後會產生大量的碎片空間,不利於空間利用率。不過CMS提供了應對策略:
    • 開啟-XX:+UseCMSCompactAtFullCollection
      開啟該參數後,每次FullGC完成後都會進行一次內存壓縮整理,將零散在各處的對象整理到一塊兒。但每次都整理效率不高,因此提供了以下參數。
    • 設置參數-XX:CMSFullGCsBeforeCompaction
      本參數告訴CMS,經過了N次Full GC過後再進行一次內存整理。

三、通用垃圾收集器——G1垃圾收集器

G1是目前最牛逼的垃圾收集器。

G1的特點

  1. 追求停頓時間
  2. 多線程GC
  3. 面向服務端應用
  4. 標記-整理和復制算法合並
    不會產生碎片內存。
  5. 可對整個堆進行垃圾回收
  6. 可預測停頓時間

G1的內存模型

G1垃圾收集器沒有新生代和老年代的概念了,而是將堆劃分為一塊塊獨立的Region。當要進行垃圾收集時,首先估計每個Region中的垃圾數量,每次都從垃圾回收價值最大的Region開始回收,因此可以獲得最大的回收效率。

Remembered Set

一個對象和它內部所引用的對象可能不在同一個Region中,那麽當垃圾回收時,是否需要掃描整個堆內存才能完整地進行一次可達性分析?

當然不是,每個Region都有一個Remembered Set,用於記錄本區域中所有對象引用的對象所在的區域,從而在進行可達性分析時,只要在GC ROOTs中再加上Remembered Set即可防止對所有堆內存的遍歷。

G1垃圾收集過程

    1. 初始標記
      標記與GC ROOTS直接關聯的對象,停止所有用戶線程,只啟動一條初始標記線程,這個過程很快。
    2. 並發標記
      進行全面的可達性分析,開啟一條並發標記線程與用戶線程並行執行。這個過程比較長。
    3. 最終標記
      標記出並發標記過程中用戶線程新產生的垃圾。停止所有用戶線程,並使用多條最終標記線程並行執行。
    4. 篩選回收
      回收廢棄的對象。此時也需要停止一切用戶線程,並使用多條篩選回收線程並行執行。

Hotspot垃圾回收器