GC算法與垃圾收集器

本文基於jdk 1.7,參考《深入了理解java虛擬機》一書

一、GC算法

垃圾自動回收機制是java語言相比c++的一大特性,但垃圾收集並不是java語言的伴生物,GC的歷史比java更加久遠。爲什麼我們要了解GC和內存分配呢,當需要排查各種內存溢出、內存泄露問題時,當垃圾收集成爲系統達到更大併發量的瓶頸時,我們就需要對垃圾自動回收機制進行調優。

1、標記-清除算法

“標記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分爲“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收掉所有被標記的對象。之所以說它是最基礎的收集算法,是因爲後續的收集算法都是基於這種思路並對其缺點進行改進而得到的。
其缺點主要有兩個:一是效率問題,標記和清除兩個過程的效率都不高;另一個是空間問題,標記和清除之後會產生大量不連續的空間碎片,空間碎片太多可能會導致,當程序在以後的運行過程中需要分配較大對象時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。

在這裏插入圖片描述

2、複製算法(年輕代的GC)

複製算法主要解決效率問題。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中一塊,當一塊內存快用完了,就將還存活的對象全部複製到另一塊內存中,然後把已使用過的那一塊內存一次清理掉,這樣每次回收都針對一整塊內存進行回收,內存分配時也不用考慮內存碎片等情況,實現簡單,運行高效。主要缺點是內存縮小爲原來的一半。在對象存活率高時,持續複製長生存期的對象則導致效率降低。
在這裏插入圖片描述
現在的商業虛擬機都採用複製算法來回收新生代。由於新生代的對象絕大部分是“朝生夕死”的,使用複製算法只需複製少量的存活對象,分配空間時也不用1:1分配,而是將內存劃分爲一塊較大的Eden區和兩塊較小的Survivor空間。HotSpot虛擬機默認Eden區和兩塊Survivor空間大小比例是8:1:1,每次使用Eden區和其中一塊Survivor空間,當回收時,將Eden區和Survivor空間還存活的對象複製到另一塊Survivor空間中,當Survivor空間不夠用時,需要依賴其他內存(老年代)進行分配擔保,大對象直接進入老年代。

3、標記-整理算法(老年代的GC)

複製收集算法在對象存活率較高時就要執行較多的複製操作,效率將會變低。並且需要額外的空間進行擔保,所以老年代一般不採用複製算法進行GC。
根據老年代的特點,有人提出了另外一種“標記-整理”(Mark-Compact)算法,標記過程仍然與“標記-清除”算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。
在這裏插入圖片描述

4、分代收集算法

當前商業虛擬機的垃圾收集都採用分代收集算法,這種算法沒用新的思想,只是根據對象的存活週期不同將內存劃分爲幾塊,一般就是把java堆分爲新生代和老年代,這樣就可以根據各個年代的特點選擇不同的算法進行垃圾收集。年輕代死亡率高,採用複製算法,老年代存活率高,並且沒有多餘的空間進行擔保,所以選擇“標記清除算法”或者“標記整理算法”。

二、垃圾收集器

垃圾收集算法是內存回收的方法論,垃圾收集器是內存回收的具體實現。
在這裏插入圖片描述

1、Serial收集器

串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。新生代、老年代使用串行回收;新生代複製算法、老年代標記-整理;垃圾收集的過程中會Stop The World(暫停其他所有工作線程)。
Serial收集器對於運行在Client模式下的虛擬機來說是一個很好的選擇。
參數控制: -XX:+UseSerialGC 串行收集器
在這裏插入圖片描述

2、ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本,新生代並行(GC線程與GC線程並行),老年代串行;新生代複製算法、老年代標記-整理算法。
除了Serial收集器,目前只有ParNew收集器能和CMS收集器配合工作。
參數控制:
-XX:+UseParNewGC ParNew收集器
-XX:ParallelGCThreads 限制線程數量
在這裏插入圖片描述

3、Parallel Scavenge收集器

Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關注系統的吞吐量(運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間))。可以通過參數來打開自適應調節策略,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量;也可以通過參數控制GC的時間不大於多少毫秒或者比例;新生代複製算法、老年代標記-整理算法。
參數控制:
-XX:+UseParallelGC 使用Parallel收集器+ 老年代串行
-XX:MaxGCPauseMillis 控制最大垃圾收集停頓時間
-XX:GCTimeRatio 設置吞吐量大小
-XX:+UseAdaptiveSizePolicy 使用自動調節策略

4、Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,同樣是一個單線程收集器,使用標記-整理算法。

5、Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法。這個收集器是在JDK 1.6中才開始提供
參數控制: -XX:+UseParallelOldGC 使用Parallel收集器+ 老年代並行

6、CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。目前很大一部分的Java應用都集中在互聯網站或B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。
從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基於“標記-清除”算法實現的,它的運作過程相對於前面幾種收集器來說要更復雜一些,整個過程分爲4個步驟,包括:

  • 初始標記(CMS initial mark)
  • 併發標記(CMS concurrent mark)
  • 重新標記(CMS remark)
  • 併發清除(CMS concurrent sweep)

其中初始標記、重新標記這兩個步驟仍然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,併發標記階段就是進行GC Roots Tracing的過程,而重新標記階段則是爲了修正併發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。

由於整個過程中耗時最長的併發標記和併發清除過程中,收集器線程都可以與用戶線程一起工作,所以總體上來說,CMS收集器的內存回收過程是與用戶線程一起併發地執行。老年代收集器(新生代使用ParNew)
優點: 併發收集,低停頓。
缺點: 佔用CPU、無法處理浮動垃圾、產生內存空間碎片
參數控制
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC後,進行一次碎片整理;整理過程是獨佔的,會引起停頓時間變長
-XX:+CMSFullGCsBeforeCompaction 設置進行幾次Full GC後,進行一次碎片整理
-XX:ParallelCMSThreads 設定CMS的線程數量(一般情況約等於可用CPU數量)
在這裏插入圖片描述

7、G1收集器

G1是目前技術發展的最前沿成果之一,HotSpot開發團隊賦予它的使命是未來可以替換掉JDK1.5中發佈的CMS收集器。與CMS收集器相比G1收集器有以下特點:

  • 空間整合:G1收集器採用標記整理算法,不會產生內存空間碎片。分配大對象時不會因爲無法找到連續空間而提前觸發下一次GC。
  • 可預測停頓:這是G1的另一大優勢,降低停頓時間是G1和CMS的共同關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度爲N毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已經是實時Java(RTSJ)的垃圾收集器的特徵了
  • 分代收集 :與其他收集器一樣,G1收集器採用分代收集算法,可以不用與其他收集器配合就能管理整個GC堆。
  • 並行與併發 :G1充分利用多CPU,多核環境下的硬件優勢,利用多個CPU來縮短Stop The World的時間。

上面提到的垃圾收集器,收集的範圍都是整個新生代或者老年代,而G1不再是這樣。使用G1收集器時,Java堆的內存佈局與其他收集器有很大差別,它將整個Java堆劃分爲多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分(可以不連續)Region的集合。
G1跟蹤各個Region裏面的垃圾堆積的價值大小(回收所獲得的空間大小和回收所需的時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間,優先收集回收價值最大的Region。
G1收集器的運作大致可分爲以下幾個步驟:

  • 初始標記(Initial Marking)
  • 併發標記(Concurrent Making)
  • 最終標記(Final Marking)
  • 篩選回收(Live Data Counting and Evaluation)

在這裏插入圖片描述

三、Minor GC與Full GC

新生代GC(Minor GC):指發生在新生代的垃圾收集動作,因爲新生代的對象都具備朝生夕死的特性,所以Minor GC非常頻繁,一般回收速度也比較快。
老年代GC(Full GC/Major GC) :指發生在老年代的垃圾收集動作,出現了Full GC,經常會伴隨至少一次的Minor GC,Full GC的速度一般比Minor GC慢10倍以上。

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