回首掏-JVM垃圾收集器

回首掏-JVM垃圾收集器

本文將對下面幾種GC回收器進行講解,前面幾個會簡單的講一下,後面兩個會重點說的。
要是哪裏有錯誤的地方,一定要指出哦,嘻嘻

  • Serial
  • Serial Old
  • ParNew
  • Parallel Scavenge
  • Parallel Old
  • CMS
  • G1

首先,我們來了解一下這幾種收集器的原理、優缺點和相應的解決方法
STW就是Stop The World
新生代爲啥選擇複製算法,而老年代不選:

因爲在新生代絕大多數的內存都是會被回收的,所以留下來的不需要回收的就很少了,所以複製算法更合適,而老年代中對象基本是比較大的,比如數組啥的,它們複製的開銷比較大,不適合選擇複製算法,而選擇標記-整理、標記-清除算法是比較好的選擇。

1、Serial

Serial收集器是發展最久的垃圾回收器,Serial單詞本身的意思是“串行”。

  • 是一個單線程的收集器
  • 作用於新生代
  • 採用複製算法
  • 在進行垃圾回收的時候會產生STW

主要適用於單CPU環境下的Client模式,目標是追求響應速度優先,可以避免多線程上下文切換帶來的消耗

2、Serial Old

Serial的老年代版本。

  • 即與Serial一樣,採用單線程垃圾回收
  • 不同的是採用的標記-整理算法
  • 進行垃圾回收時會產生STW
  • 作用於老年代

主要適用於單CPU環境下的Client模式、CMS的後備預案,目標是追求響應速度優先

3、ParNew

ParNew是Serial的多線程版本。

  • 即使用多線程並行進行垃圾回收
  • 作用於新生代
  • 採用複製算法
  • 進行垃圾回收時會產生STW

主要適用於多CPU環境時在Server模式下與CMS配合,目標是追求響應速度優先

4、Parallel Scavenge

Parallel Scavenge與ParNew類似,只是關注點不同。Parallel Scavenge主要關注

  • 吞吐量。吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)。提供了兩個參數來控制吞吐量

-XX:MaxGCPauseMillis 控制最大垃圾收集停頓時間,大於0的毫秒數
-XX:GCTimeRatio 設置吞吐量大小,大於0且小於100的整數,即垃圾收集時間佔總時間的比率

  • 使用多線程並行進行垃圾回收
  • 作用於新生代
  • 採用複製算法
  • 進行垃圾回收時會產生STW

Parallel Scavenge收集器的目標是達到一個可控制的吞吐量(Throughput)
我們知道,停頓時間越短就越適合需要與用戶交互的程序,良好的響應速度能提升用戶體驗。
而高吞吐量則可以高效率地利用CPU時間,儘快完成程序的計算任務,主要適合在後臺計算而不需要過多交互的任務。
主要適用於在後臺運算而不需要太多交互的任務,目標是追求吞吐量優先

5、Parallel Old

Parallel Old是Parallel Scavenge的老年代版本

  • 多線程並行收集
  • 使用標記-整理算法
  • 作用於老年代
  • 進行垃圾回收時會產生STW

主要適用於在後臺運算而不需要太多交互的任務,目標是追求吞吐量優先 ,JDK8中默認的選擇是"-XX:+UseParallelGC",是 Parallel Scavenge + Parallel Old組合。

6、CMS(Concurrent Mark Sweep)

  • 以獲取最短回收停頓時間爲目標
  • 作用於老年代
  • 使用標記-清除算法
  • 會產生空間碎片,但可以通過配置 -XX:+UseCmsCompactAtFullCollection (默認已開啓)用於在CMS收集器要進行FullGC時開啓內存碎片的整理

CMS特點: 併發收集,低停頓。

它的標記-清除算法過程:

  1. 初始標記,會導致STW
  2. 併發標記,與用戶線程同時運行;
  3. 預清理,與用戶線程同時運行;
  4. 可被終止的預清理,與用戶線程同時運行;
  5. 重新標記 ,會導致STW
  6. 併發清除,與用戶線程同時運行;

其實只要關注四個就差不多了,簡單點就是:

  • 初始標記:只標記GC Roots直接關聯到的對象,速度快,需要“Stop The World”。
  • 併發標記:根據GC Roots標記的對象進行追蹤,標記出需要回收的對象。過程較長,但GC線程與用戶線程併發執行,不產生 STW
  • 重新標記:修復併發標記過程中用戶程序的運行導致標記的對象產生變動的的那部分對象,會產生STW
  • 併發清除:清除標記的對象,與用戶線程併發執行,不產生STW

這裏介紹一下一般可以作爲GC Roots的對象是什麼

虛擬機棧(棧幀中的局部變量表,Local Variable Table)中引用的對象
方法區中類靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中(native方法)引用的對象

上圖從https://my.oschina.net/u/4349287/blog/3225344中來

CMS缺點:

  • 垃圾碎片的問題,我們都知道CMS是使用的是標記-清除算法的,所以不可避免的會出現垃圾碎片的問題
  • 一般CMS的GC耗時80%都在remark階段,remark階段停頓時間會很長,在CMS的這四個主要的階段中,最費時間的就是重新標記階段
  • concurrent mode failure: 也就是CMS收集器無法處理浮動垃圾(Floating Garbage)產生的, 這個異常發生在CMS正在回收的時候。執行CMS GC的過程中,同時業務線程也在運行,當年輕帶空間滿了,執行Young GC時,需要將存活的對象放入到老年代,而此時老年代空間不足,這時CMS還沒有機會回收老年帶產生的,或者在做Minor GC的時候,新生代救助空間放不下,需要放入老年代,而老年代也放不下而產生的。這時虛擬機可能就會啓動後備預案,臨時啓用Serial Old收集老年代,這樣停頓時間很長。

由於CMS併發清理時,用戶線程還在運行,伴隨產生新的垃圾,而這一部分垃圾出現在標記之後,只能下次GC時再清理。這一部分垃圾就稱爲”浮動垃圾“。

  • promotion failed: 在進行Minor GC時,Survivor空間不足,對象只能放入老年代,而此時老年代也放不下造成的,多數是由於老年代有足夠的空閒空間,但是由於碎片較多,新生代要轉移到老年帶的對象比較大,找不到一段連續區域存放這個對象導致的
    CMS收集器對CPU資源非常敏感: 默認啓動的回收線程數是(CPU+3)/4. 當CPU 4個以上時,併發回收垃圾收集線程不少於25%的CPU資源。
    解決辦法:

  • 垃圾碎片的問題: 針對這個問題,這時候我們需要用到這個參數: -XX:CMSFullGCsBeforeCompaction=n 意思是說在上一次CMS併發GC執行過後,到底還要再執行多少次 full GC纔會做壓縮。默認是0,也就是在默認配置下每次CMS GC頂不住了而要轉入full GC的時候都會做壓縮,開啓內存碎片合併整理,內存整理過程是無法併發的,空間碎片問題沒了,但停頓時間變長。

  • concurrent mode failure: 只需要設置兩個參數即可

-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=60:是指設定CMS在對內存佔用率達到60%的時候開始GC。G1中也有個類似的。
由於在垃圾收集階段用戶線程還需要運行,那也就還需要預留有足夠的內存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集。
當然也不能設置過高,比如90%,這時候雖然GC次數少,但是,卻會導致用於用戶線程空間小,效率不高,太低10%,你自己想想會怎麼樣,體會體會!

  • remark階段停頓時間會很長的問題: 解決這個問題巨簡單,加入 -XX:+CMSScavengeBeforeRemark。在執行remark操作之前先做一次 Young GC,目的在於減少年輕代對老年代的無效引用,降低remark時的開銷

CMS什麼時候FUll GC

  1. 舊生代空間不足

1.在新生代對象轉入及創建爲大對象、大數組時纔會出現不足的現象
2.當執行Full GC後空間仍然不足,則拋出如下錯誤:java.lang.OutOfMemoryError: Java heap space
爲避免以上兩種狀況引起的FullGC,調優時應儘量做到讓對象在Minor GC階段被回收、讓對象在新生代多存活一段時間及不要創建過大的對象及數組。

  1. Permanet Generation空間滿

PermanetGeneration中存放的爲一些class的信息等,當系統中要加載的類、反射的類和調用的方法較多時,Permanet Generation可能會被佔滿,在未配置爲採用CMS GC的情況下會執行Full GC。如果經過Full GC仍然回收不了,那麼JVM會拋出如下錯誤信息:java.lang.OutOfMemoryError: PermGen space
爲避免Perm Gen佔滿造成Full GC現象,可採用的方法爲增大Perm Gen空間或轉爲使用CMS GC。

  1. CMS GC時出現promotion failed和concurrent mode failure
  2. 統計得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間

Hotspot爲了避免由於新生代對象晉升到舊生代導致舊生代空間不足的現象,在進行Minor GC時,做了一個判斷,如果之前統計所得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間,那麼就直接觸發Full GC。

G1這個另類下一篇再說,先看看上面這幾種收集器的組合使用:

收集器組合

收集器大致可以按這張圖進行組合使用:
在這裏插入圖片描述
接下來,直接來一張圖,簡單明瞭。
在這裏插入圖片描述

本文參考https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-cms-la-ji-hui-shou-qi.html

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