深入理解JVM(五)——HotSpot垃圾收集器詳解

原文:http://blog.csdn.NET/u010425776/article/details/51199767


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. 篩選回收 
    回收廢棄的對象。此時也需要停止一切用戶線程,並使用多條篩選回收線程並行執行
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章