漫談jvm垃圾收集(2)

上一篇博文中我們說了,哪些對象該被回收已經在程序運行到什麼地方進行回收。接下來我們就來討論怎麼回收這些不在使用的對象。

(1)常用的幾個垃圾收集算法

  • 標記-清除

從GC Roots開始標記所有被引用的對象,之後遍歷整個回收區刪除標記的對象
缺點:標記和刪除的效率都挺低;並且會產生大量內存碎片

  • 複製算法

把內存分爲兩塊,只使用其中一塊,回收時先把存活的對象複製到另一塊內存,之後清空前一塊已經使用的內存。

優點:簡單、高效

缺點:需要浪費一定的內存

  • 標記-整理

先標記存活對象,之後將所有存活對象都移動到一邊,最後清除掉邊界之外的內存。

這種回收算法適合回收前內存中存在大量對象的情況,對應就是分代收集策略中的老年代。

目前大多數商業使用的jvm,都是根據對象存活週期的不同,將堆區分爲新生代和老年代,同時老年代爲新生代提供內存分配擔保,大量“朝生夕死”的對象一般都放到新生代,由於新生代的大部分對象都會在一次Minor GC中死亡,存活的對象很少,所以新生代的GC收集器都採用了複製算法。新生代分爲Eden 、 S0 、 S1區,其中S0、S1稱爲Surivivor區。 S0和S1就是用來實現複製的,在任何一次Minor GC後,S0和S1總是隻有一個區域有數據,另一個區域爲空,以便於下一次複製使用,大對象或者新生代無法容下的對象晉升到老年代。

(2)垃圾收集器

HotSpot虛擬機的垃圾收集器如下圖所示:



  • Serial 是新生代的串行收集器
它使用的是複製算法,在執行垃圾收集時全程stop the world,只使用一個線程去完成垃圾收集工作。

  • Serial Old是Serial在老年代的版本
它使用的是標記-整理算法,同樣在執行垃圾收集時全程stop the world,只使用一個線程去完成垃圾收集工作。
同時這個收集器在老年代還有一個重要用途,就是作爲CMS收集器在發生Concurrent Mode Failure時的後備收集器。
  • ParNew收集器
這是新生代收集器,他也是使用複製算法在執行垃圾收集時全程stop the world,只不過他使用多個線程去完成垃圾收集工作,其他屬性幾乎都與Serial收集器一樣,只不過是Serial的多線程版本。
  • Parallel Scavenge收集器(吞吐量優先收集器)
這是新生代收集器,使用複製算法,同時又是多線程收集器,看起來很像ParNew,但是Parallel Scavenge的關注點不同,其他收集器都是儘可能的把stop the world的時間壓縮的儘量短,而Parallel Scavenge的目標是能達到一個可以控制的吞吐量,吞吐量=用戶運行代碼的時間/(用戶運行代碼的時機+GC線程運行時間)。對於計算密集型的應用可能會考慮計算的吞吐量,這時候可以使用Parallel Scavenge收集器來保證吞吐量。
  • Parallel Old收集器
這是老年代收集器,使用標記-整理算法,多線程執行垃圾收集。它是Parallel Scavenge的老年代版本。
  • CMS收集器(Concurrent Mark Sweep)併發收集、低停頓
這是老年代收集器,使用標記-清除算法,多線程收集器。它執行垃圾收集的整個過程圖示如下:

它主要分爲四個階段:
初始標記:stop the world,gc線程僅僅只是標記一下GC Roots直接關聯的對象,速度很快
併發標記:GC線程與用戶線程併發執行,這期間對象的引用關係可能再次發生變化,所以需要再次下階段的重新標記
重新標記:stop the world,修正併發標記期間導致的引用關係變化
併發清除:GC線程與用戶線程併發執行,GC線程會搶佔一部分CPU資源,導致用戶程序變慢。
CMS執行一次垃圾回收,會出現2次 stop the world。
由於在併發清理階段用戶線程還在運行,所以CMS收集器不能等老年代都填滿了再進行收集,它需要預留出一定的空間給用戶線程運行,可以通過-XX:CMSInitiatingOccupancyFraction參數來指定,當老年代使用了百分之多少的時候執行併發清理。
但是如果老年代預留的空間太小,不能滿足用戶線程分配空間的需求,那麼這時候就會發生Concurrent Mode Failure 失敗。這是虛擬機會啓用Serial Old 收集器stop the world,暫停用戶線程執行Full GC,這樣性能會大大降低。

由於CMS使用了標記-清理算法,這會產生內存碎片,CMS提供了兩個相關參數用於優化內存碎片現象,
-XX:UseCMSCompacAtFullCollection在Full GC時執行內存碎片整理,
-XX:CMSFullGCsBeforeCompaction執行多少次不壓縮的Full GC之後,執行一次帶壓縮的Full GC

總結一下:
 (1)Serial, ParNew, Parallel Scanvange, Parallel Old, Serial Old全程都會Stop the world,JVM這時候只運行GC線程,不運行用戶線程。
(2)CMS GC線程可以和用戶線程並執行,所以Web應用是使用CMS收集器的一個重要場景
(3) Parallel Scanvange, Parallel Old是注重吞吐量優先的收集器,如果是計算密集型的服務可以考慮選擇它們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章