如果說垃圾收集算法是Java虛擬機的指導思想,那麼下面的垃圾收集器正是這些算法的實現,保證了jvm之運行時內存的正確使用和定時清理
一、新生代收集器
1、Serial
複製算法。
Serial收集器是最基本的收集器。
- 單線程
- stop the world
簡單高效,沒有線程交互的開銷,適用於新生代的垃圾收集
2、ParNew
複製算法。
ParNew其實就是Serial收集器的多線程版本
- 多線程
- Stop the world
-XX:+UseConcMarkSweepGC使用CMS併發收集器的默認新生代收集器就是ParNew,也可以使用-XX:UseParNewGc強制指定
3、Parallel Scavenge
複製算法。
並行多線程收集器,它的關注點在於提高用戶程序的吞吐量,吞吐量就是JVM運行用戶程序代碼的時間 / (用戶程序代碼時間 + 垃圾回收時間)。
JVM提供2個參數用於精確控制吞吐量:
* -XX:MaxGCPauseMillis
* -XX:GCTimeRatio
MaxGCPauseMillis垃圾收集最大停頓的時間,毫秒爲單位。如果設置太小則會引起GC的次數變多
GCTimeRation配置垃圾收集的時間佔比,相當於吞吐量的倒數。如果參數設置19,那麼允許最大GC的時間佔總時間的5%(也就是1/(1+19))。默認值爲99,也就是允許最大1%的垃圾收集時間(1 / ( 1 + 99 ))
智能參數:-XX:+UseAdaptiveSizePolicy
如果啓用這個參數,就不需要手工指定新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRatio)、晉升到老年代對象年齡(-XX:PretenureSizeThreshold) 等細節,JVM會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量。
二、老年代收集器
1、CMS收集器
CMS(Concurrent Mark Sweep) 收集器是一種以獲取最短回收停頓時間爲目標的收集器,希望在發生收集時系統停頓的時間最短。
整個過程分爲4步:
- 初始標記
- 併發標記
- 重新標記
- 併發清除
初始標記,重新標記需要“Stop the world”
- 初始標記僅標記GC Roots,速度很快
- 併發標記GC Roots的追蹤
- 重新標記修正併發標記期間用戶繼續運作而導致標記產生變動的那一部分對象(這個階段停頓時間比初始標記長,但比並發標記短)
CMS的缺點
佔用一部分線程,或者CPU資源,影響系統吞吐量。默認:(cpu數量+3)/4,如果CPU爲1核或者2核時,採用CMS會對用戶程序產生影響
無法清除浮動垃圾。併發清理階段,用戶線程還繼續運行,這時會繼續產生垃圾,CMS無法回收這部分的垃圾,只能留到下一次,這種叫做浮動垃圾。
標記-清除算法,意味着垃圾收集會出現內存碎片。
針對CMS的缺點JVM做的努力
CMS佔用用戶線程,使得用戶程序的吞吐量降低。爲解決這個問題,虛擬機提供“增量式併發收集器”,是併發標記、併發清理的GC線程和用戶線程搶佔式交替執行 但是這種方案效果不明顯,廢棄了
CMS垃圾收集時,由於用戶線程還繼續運行,所以JVM必須要留足夠的內存空間給用戶線程使用,不能像其他收集器一樣等老年代幾乎被填滿了再收集。JDK5默認內存空間使用到68%時會被激活,可以通過參數調節-XX:CMSInitiatingOccupancyFraction,JDK6默認92%。如果CMS運行期間,預留的內存無法滿足程序的需要,會出現‘Concurrent Model Failure’失敗,這時啓動Serial Old收集器收集內存垃圾
CMS會產生內存碎片,如果剩餘的內存中無法分配給足夠大的對象,會提前觸發Full GC,可以通過-XX:+UseCMSCompactAtFullCollection配置,當JVM頂不住要進行Full GC時開啓內存碎片整理,但是GC的時間變長了。0表示Full GC時都進行內存碎片整理
2、Serial Old收集器
“標記-整理”算法
- 單線程
- 可以作爲CMS收集的後備預案
就是當發生“Concurrent Model Fail”,也就是在垃圾收集過程中,如果剩餘的內存空間無法分配給用戶程序使用,則會啓動該手機算法進行老年代的垃圾收集
3、Parallel Old收集器
“標記-整理”算法
- 多線程
配合新生代的Parallel Scavenge收集器,因爲新生代的Parallel Scavenge只能選擇Serial Old不能選擇CMS,導致收集性能地,所以出現Parallel Old地老年代採用多線程的回收