文章目錄
垃圾回收器
1. 分類
-
按線程數:串行垃圾回收器和並行垃圾回收器
串行回收器指同一時間段內只允許一個CPU用於執行垃圾回收操作,此時工作線程被暫停,直至垃圾收集工作結束。並行回收器指可以運用多個CPU同時執行垃圾回收,可以提升應用的吞吐量 。
-
按工作模式:併發式垃圾回收器和獨佔式垃圾回收器。併發式指回收器和應用程序線程之間交替執行,儘可能減少應用程序的停頓時間;獨佔式一旦開始運行就會停止應用程序中的所有用戶線程,直到垃圾回收工作執行結束。
-
按碎片處理方式:壓縮式垃圾回收器和非壓縮式垃圾回收器
-
按工作區域:新生代垃圾回收器和老年代垃圾回收器
2. 性能指標
- 吞吐量:運行用戶程序的時間佔總運行時間的比例
- 垃圾收集開銷:垃圾收集所用時間與總運行時間的比例
- 暫停時間:執行垃圾收集時,程序的工作線程被暫停的時間
- 收集頻率:相對於應用程序的執行,收集操作發生的頻率
- 內存佔用:Java堆區所佔內存的大小
- ……
其中最爲重要的就是平衡吞吐量和暫停時間。如果以吞吐量優先,那麼必然需要降低內存回收的執行效率,但這樣會導致需要更長的暫停時間來執行內存回收;如果以低延遲優先爲原則,那麼爲了降低每次執行內存回收時的暫停時間,就只能頻繁的執行內存回收,這又引起了年輕代內存的縮減和導致程序吞吐量下降。
一般來說,在最大吞吐量優先的情況下,降低停頓時間。
2.1 吞吐量
吞吐量(throughput)指CPU用於運行用戶程序的時間與CPU總消耗時間的比值,即
高吞吐量的程序有更長的時間基準,不太考慮快速響應。
2.2 暫停時間
暫停時間(pause time)指一個時間段內應用程序線程暫停,讓GC線程執行的狀態。
3. 概述
Java中已有的垃圾回收器有:
- 串行回收器:Serial、Serial Old
- 並行回收器:ParNew、Parallel Scavenge、Parallel Old
- 併發回收器:CMS、G1
按照垃圾回收器所在的內存區域可分爲:
- 新生代回收器:Serial、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old、Parallel Old、CMS
- 整堆回收器:G1
如上圖所示,兩個回收器之間有連線表示可搭配使用,其中實現表示現在仍可使用的組合,虛線表示已經棄用的組合,以最新版本的Java說明。
通過-XX:+PrintCommandLineFlags
查看命令行相關參數,然後使用jinfo -flag 相關垃圾回收器參數 進程ID
來查看此時JDK版本默認的垃圾收集器。
4. Serial回收器 - 串行回收
Serial回收器是最基本、歷史最悠久的垃圾回收器,也是HotSpot中Client模式下默認的新生代垃圾回收器。Serial回收器使用複製算法、串行回收和STW機制的方式執行內存回收。
除了新生代之外,Serial Old用於執行老年代垃圾收集工作,它同樣採用了串行回收和STW機制,只不過內存回收算法選擇的是標記-整理算法。
Serial回收器的優點在於簡單而高效,對於限定單個CPU的環境來說,Serial回收器由於沒有線程交互的開銷,可以獲得最高的單線程收集效率。
HotSpot可以使用-XX:+UseSerialGC
參數可以指定新生代和老年代分別使用Serial GC和Serial Old GC。
5. ParNew回收器 - 並行回收
ParNew可以看做是是Serial回收器的並行版本,它同樣用於新生代的垃圾回收,同時也是很多JVM在Server模式下新生代的默認垃圾回收器。
對於新生代,回收次數頻繁,使用並行方式高效;對於老年代,回收次數少,使用串行方式節省資源。
可以使用XX:+UserParNewGCC
手動指定使用ParNew回收器執行內存回收任務,-XX:ParallelGCThreads
限制線程數量,默認開啓和CPU數據相同的線程數。
6 . Parallel回收器 - 吞吐量優先
6.1 概述
Parallel Scanvenge回收器採用了複製算法、並行回收和STW機制執行新生代的垃圾回收工作。相比於ParNew而言,它的目標則是達到一個可控制的吞吐量,而且採用了自適應調節策略。對應的老年代的回收器爲Parallel Old回收器,它採用了標記-整理算法,同時也採用了並行回收和STW機制。
在程序吞吐量優先的應用場景中,Parallel回收器和parallel Old回收器的組合在Server模式下的內存回收性能很優秀,因此,它也是Java8中默認的垃圾回收器。
6.2 相關參數
-
-XX:MaxGCPauseMillis
:設置垃圾回收器的最大暫停時間 -
-XX:GCTimeRatio
:垃圾回收時間佔總時間的比例,用於衡量吞吐量的大小,默認值爲99 -
-XX:+UseAdaptiveSizePolicy
:設置回收器具有自適應調節策略。在這種模式下,新生代的大小、Eden和Survivor的比例、晉升老年代的對象年齡等參數會被自動調整,已達到在堆大小、吞吐量和停頓時間之間的平衡點 -
-XX:UseParallelGC
:手動指定新生代使用Parallel回收器 -
-XX:+UseParallelOldGC
:手動指定老年代使用Parallel Old回收器-XX:UseParallelGC
和-XX:+UseParallelOldGC
在Java中默認開啓,兩個參數互相激活。 -
-XX:ParallelGCThreads
:設置新生代垃圾回收器的線程數- 默認情況下,當CPU數量小於8個,它的值等於CPU的數量
- 當CPU數量大於8個,它的值等於3 + [5 * CPU_Count] / 8
7. CMS回收器
7.1 概述
CMS(Concurrent-Mark-Sweep)回收器第一次實現了垃圾收集線程和用戶線程同時工作,它關注的重點是如何儘可能的縮短垃圾回收時用戶線程的停頓時間。停頓時間越短,響應速度越快,用戶體驗越好。
CMS採用的是標記-清除算法,並且也會有STW。它作爲老年代的垃圾回收器來用時。新生代的垃圾回收器只能從ParNew和Serial中二選一。
7.2 工作原理
CMS的垃圾回收階段分爲4個主要的過程,即初始標記階段、併發標記階段、重新標記階段和併發清除階段:
- 初始標記階段(Initial-Mark):程序所有的工作線程被STW所暫停,這個階段僅僅只是標記出GC Roots能直接關聯到的對象,一旦標記完成之後就會恢復之前被暫停的所有應用線程。由於直接關聯對象比較小,因此速度很快
- 併發標記階段(Concurrent-Mark):從GC Roots的直接關聯對象開始遍歷整個對象圖的過程,耗時較長但不需要暫停應用線程,它們可以和垃圾回收線程併發執行
- 重新標記階段(Remark):由於在併發標記階段中程序的工作線程還會和垃圾回收線程同時執行或者交叉執行,因此因用戶程序繼續執行而導致標記產生變動的那一部分對象就會發生改變,因此需要重新標記的過程。它執行時間較長,但遠比並發標記階段耗時少
- 併發清除階段(Concurrent-Sweep):清除標記階段判斷已經死亡的對象,並釋放內存空間,它可以和用戶線程同時併發執行
初始標記和重新標記的存在,使得CMS仍在存在STW。但併發標記和併發清除階段不需要暫停其他的用戶線程,因此整體的回收是低停頓的。
由於垃圾回收階段用戶線程並沒有中斷,所以在CMS回收過程中換應該確保用戶線程有足夠的內存可用。因此,CMS不是當老年代完全沒有空間可用時才被觸發,而是當堆內存的使用率達到一個閾值時,便開始進行回收,確保應用程序在CMS工作過程中依然有足夠的空間支持應用程序運行。
如果CMS運行期間預留的內存空間不夠用戶程序使用,那麼就會出現一次"Concurrent Mode Failure"失敗,此時虛擬機將會臨時啓用Serial Old作爲老年代的垃圾回收器使用,這樣停頓時間就長了。
CMS GC 的優點:
- 併發收集
- 低延遲
不足之處有:
- 會產生內存碎片:標記-清除算法不可避免的會產生碎片,此時內存分配只能使用空閒列表法
- 對CPU資源非常敏感:在併發階段時,它雖然不會導致用戶線程暫停,但是也會因爲佔用一部分線程而導致應用程序變慢,總吞吐量下降
- 無法處理浮動垃圾:在併發標記階段由於垃圾回收線程和用戶線程是同時運行或交叉運行的,那麼在併發標記階段如果產生新的垃圾對象,CMS將無法對這些垃圾進行標記,最終會導致這些新產生的垃圾對象無法被及時回收,只能等到下一次GC時被回收
7.3 相關參數
-XX:UseConcMarkSweepGC
:手動指定使用CMS,此時的組合爲ParNew+CMS+Serial OLD(後備方案)-XX:CMSInitiatingOccupanyFraction
:設置堆內存使用率的閾值,一旦達到該閾值,並開始進行回收,默認值爲68-XX:+UseCMSCompactAtFullCollection
:用於指定在執行完Full GC後堆內存空間是否進行壓縮整理。避免內存碎片的產生,它會導致停頓時間變長-XX:CMSFullGCsBefoerCompaction
:設置在執行完多少次Full GC後對內存空間進行壓縮整理-XX:ParallelCMSThreads
:設置CMS的線程數,默認是(ParallelGCThreads + 3) / 4
避免內存碎片的產生,它會導致停頓時間變長
-XX:CMSFullGCsBefoerCompaction
:設置在執行完多少次Full GC後對內存空間進行壓縮整理-XX:ParallelCMSThreads
:設置CMS的線程數,默認是(ParallelGCThreads + 3) / 4