垃圾收集算法和垃圾收集器

垃圾收集算法

(畫圖的工具使用Gliffy,提供的模塊有限使用3(高)*6(寬)的格子來描述算法的思想最理想的是寬爲8的格子)

標記-清除算法(Mark-Sweep)

標記-清除算法是最基礎的收集算法(之所以叫做基礎,是因爲後續算法都是基於此針對他的不足改進得到的),如同他的名字一樣,分爲兩個階段:標記階段、清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所佔用的空間
如下圖:
標記清除

缺點或者不足:效率問題,標記清除效率都不高;另一個是空間問題,容易產生內存碎片,碎片太多可能會導致後續過程中需要爲大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。

複製算法(Copying)

爲了解決標記清除算法的效率問題,複製算法出現了。
他將可用內存按容量分爲等大的兩塊,每次只是用其中一塊,另一塊作爲保留區域,當一塊內存用完了,就將存活的對象複製到另一塊上,然後把已使用過的內存空間一次清理掉,如下圖:
複製算法
複製算法實現簡單,運行高效且不容易產生內存碎片
缺點不足:1.對內存空間的使用做出了高昂的代價,因爲能夠使用的內存縮減到原來的一半;2.複製算法的效率跟存活對象的數目多少有很大的關係,如果存活對象很多,那麼複製算法的效率將會大大降低。

使用:由於他的特性,現代商業虛擬機採用複製算法來回收新生代,IBM公司研究表明,新生代中98%的對象是“朝生夕死”的,所以內存空間不必按照1:1的比例來分配,而是將內存劃分爲一塊較大的Eden空間,兩塊較小的Survivor空間(HotSpot虛擬機默認Eden空間和Survivor空間的比例爲8:1,80% + 10% = 90%),每次使用Eden空間和其中一塊Survivor空間,當回收時,先將Eden和Survivor上存活的對象複製到,空閒的Survivor上,然後統一清理Eden空間和剛纔是用過的Survivor空間。

但是,新生代98%的對象可回收針對一般場景,沒有辦法保證每次都有不多於10%的對象存活(當另一塊Survivor空間不夠用來存放上一次新生代收集的存活對象時,這些對象需要被分配進入擔保內存),所以需要其他內存(老年代)進行分配擔保。

標記-整理算法(Mark-Compact)

和複製算法不同,標記-整理算法是針對老年代特點(存活率較高)的算法,充分利用空間,標記過程和“標記-清除算法”中的過程一樣,但是在完成標記之後,它不是直接清理可回收對象,而是將存活對象都向一端移動,然後清理掉端邊界以外的內存。如下圖:

標記整理

老年代的垃圾回收器(例如 Serial Old,Parallel Old,不包括CMS,CMS使用的是標記清除法)都是採用這個算法,主要由於老年代的對象都比較持久,不是短暫的。

分代收集算法(Generational Collection)

根據對象存貨週期的不同劃分爲幾塊,一般把堆分爲新生代和老年代,還有一塊區域被稱爲永久代即方法區(非堆,它的回收主要是針對廢棄常量和無用類)。
在新生代中,每次垃圾回收時有大量對象死去,只有少量存活,那就選擇“複製算法”,只需要付出少量對象的複製成本就可以完成收集。
在老年代中,存活率高,沒有額外的空間對它分配擔保,就必須使用“標記清除”或者“標記整理”來回收。

垃圾收集器

此處討論的垃圾收集器基於《深入理解Java虛擬機》中所依賴的Java 7 update14之後的HotSpot虛擬機(在這個版本中正式提供了G1(Garbage First)收集器),一共七種,Serial和Serial Old在一起介紹。

先來一張圖:

HotSpot虛擬機新生代老年代收集器劃分搭配圖

1.Serial/Serial Old

  Serial/Serial Old收集器是最基本最古老的收集器(JDK1.3.1之前版本),它是一個單線程收集器,並且在它進行垃圾收集時,必須暫停所有工作線程(Stop The World)。Serial收集器是針對新生代的收集器,採用的是Copying算法,Serial Old收集器是針對老年代的收集器,採用的是Mark-Compact算法。它的優點是實現簡單高效,但是缺點是會給用戶帶來停頓。

2.ParNew

  ParNew收集器是Serial收集器的多線程版本,使用多個線程進行垃圾收集。

3.Parallel Scavenge(吞吐量優先收集器)

  Parallel Scavenge收集器是一個新生代的多線程收集器(並行收集器),它在回收期間不需要暫停其他用戶線程,其採用的是Copying算法,該收集器與前兩個收集器有所不同,它主要是爲了達到一個可控的吞吐量(吞吐量:就是CPU用於運行用戶代碼的時間與CPU總耗時的比值 吞吐量 = 運行用戶代碼時間/(運行用戶代碼時間+垃圾回收時間),比如虛擬機總共運行100分鐘,垃圾回收用了1分鐘,吞吐量是99%吞吐量越高應用程序交互性越好)。

4.Parallel Old

  Parallel Old是Parallel Scavenge收集器的老年代版本(並行收集器),使用多線程和Mark-Compact算法。(JDK1.6中提供)

5.CMS

  CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器,併發收集、低停頓,它是一種併發收集器,採用的是Mark-Sweep算法。
步驟交標記清除複雜有四步:
①初始標記
②併發標記
③重新標記
④併發清除
缺點:
①對CPU資源敏感(佔用CPU資源較多)。
②無法處理浮動辣雞(Floating Garbage,即在垃圾回收是又產生的一部分垃圾,在當次回收時無法處理只能留待下次處理),可能出現Concurrent Mode Failure失敗,從而觸發另一次FullGC。
③標記清除產生大量碎片。
6.G1
詳細參考:新一代垃圾回收算法增量收集算法和G1收集算法
  G1(Garbage First)收集器是當今收集器技術發展最前沿的成果,它是一款面向服務端應用的收集器,它能充分利用多CPU、多核環境。因此它是一款並行與併發收集器,並且它能建立可預測的停頓時間模型。

特點:並行與併發  分代收集  空間整合  可預測停頓
步驟: ①初始標記 ②併發標記 ③最終標記 ④篩選回收

  下面補充一下關於內存分配方面的東西:
  
  對象的內存分配,往大方向上講就是在堆上分配,對象主要分配在新生代的Eden Space和From Space,少數情況下會直接分配在老年代。如果新生代的Eden Space和From Space的空間不足,則會發起一次GC,如果進行了GC之後,Eden Space和From Space能夠容納該對象就放在Eden Space和From Space。在GC的過程中,會將Eden Space和From Space中的存活對象移動到To Space,然後將Eden Space和From Space進行清理。如果在清理的過程中,To Space無法足夠來存儲某個對象,就會將該對象移動到老年代中。在進行了GC之後,使用的便是Eden space和To Space了,下次GC時會將存活對象複製到From Space,如此反覆循環。當對象在Survivor區躲過一次GC的話,其對象年齡便會加1,默認情況下,如果對象年齡達到15歲,就會移動到老年代中。

  一般來說,大對象會被直接分配到老年代,所謂的大對象是指需要大量連續存儲空間的對象,最常見的一種大對象就是大數組,比如:

  byte[] data = new byte[4*1024*1024]

  這種一般會直接在老年代分配存儲空間。

  當然分配的規則並不是百分之百固定的,這要取決於當前使用的是哪種垃圾收集器組合和JVM的相關參數。

參考:
《深入理解Java虛擬機》第二版
推薦要看的虛擬機方面的知識
垃圾回收算法和垃圾收集器
新一代垃圾回收算法增量收集算法和G1收集算法

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