JVM從零開始(三)-經常使用垃圾回收器及原理

1、垃圾回收器有哪些

因爲有些年輕代和老年代回收器沒法兼容,通常使用都是用如下四個組合。java

查看命令

java -XX:+PrintCommandLineFlags -version算法

第一個組合:Serial + Serial Old

Serial做爲年輕代回收器和Serial Old垃圾回收器(JDK1.3版本以前)做爲老年代回收器。在JDK1.3版本以前是惟一選擇,如今基本不用,由於是單進程收集器,沒有發揮出如今多核並行處理的優點。併發

第二個組合:ParNew + CMS

ParNew做爲年輕代回收器,CMS做爲老年代回收器,通常須要手動指定,由於如今jdk7,8默認不是使用這個策略。而是使用的下面的Parallel Scavenge + Parallel Old。其基本收集原理和下面的Parallel Scavenge + Parallel Old沒有區別,區別在於Parallel Scavenge和 Parallel Old有自適應調節策略,直接能夠適應最大吞吐量。但忽略了停頓時間,不適用於要求用戶體驗的場景,個別請求可能等待時間較長。而ParNew + CMS主要場景是注重控制單次回收停頓時間。性能

第三個組合:Parallel Scavenge + Parallel Old

JDK6版本以後引入,Parallel Scavenge做爲年輕代回收器,Parallel Old做爲老年代回收器,在JDK6以前,Parallel Scavenge只能適配Serial Old,如今是JDK7,JDK8的默認組合。特色上面說了就是適應最大吞吐量。線程

第四個組合:G1回收器

G1回收器,JDK7出現,JDK9以後的默認回收器,老年代和年輕代均可以回收,特色是直接對停頓時間進行設置。code

2、各回收器的細節原理

上一篇就介紹了垃圾回收機制,可是沒有針對各個回收器進行區別,實際上上一篇文章就是基於ParNew + CMS來介紹的,其餘垃圾回收器包括Serial + Serial Old,Parallel Scavenge + Parallel Old基本上是相同的機制。只不過Serial + Serial Old是單線程回收,全程阻塞用戶線程,Scavenge + Parallel Old更注重吞吐量,自適應調節,不須要手動指定新生代的大小,Eden和Survivor區的比例,晉升老年代對象等細節參數。但G1會稍稍有點不同。對象

這裏主要分兩塊來說,分別以ParNew + CMS和G1回收器來介紹下其中在第二篇文章沒有提到的細節以及G1與其的區別。blog

ParNew + CMS 回收機制

1.相關參數

  • -XX:+UseParNewGC 指定使用ParNewGC
  • -XX:ParallelGCThreads 指定ParNewGC線程數量,通常不指定ParNewGC會自動根據CPU核數適配
  • -XX:+UseConcMarkSweepGC 指定使用CMS回收器
  • -XX:CMSInitiatingOccupancyFaction 老年代達到多少比例觸發CMS回收(oldGC),主要是爲了不浮動垃圾(oldGC過程當中進來的老年代對象)太多致使強制觸發Serial Old回收(默認值92%)
  • -XX:+UseCMSCompactAtFullCollection CMS默認打開,FULLGC以後進行碎片整理
  • -XX:CMSFullGCsBeforeCompaction 執行多少次FullGC以後進行一次內存整理,默認爲0,意思是每次FUllGC以後都進行整理

2.ParNew的回收機制

parNew的回收機制跟上篇文章介紹的相同,採起復制算法清理。parNew的特色是並行進行垃圾回收,充分利用現代處理器的多核優點。值得注意的是並行並非和用戶線程並行,在垃圾回收階段仍是會阻塞用戶線程。另外能夠利用上面的-XX:ParallelGCThreads參數指定並行線程數量,不指定的話ParNewGC會自動根據CPU核數適配。進程

3.CMS的回收機制

回收分爲四個階段:內存

  • 初始標記階段 阻塞用戶線程,標記GCROOTS直接引用的對象
  • 併發標記階段 不阻塞用戶線程,儘量標記GCROOTS間接引用的對象,因爲同時在建立新對象,這部分新進入老年代的對象可能不會被標記。該階段是四個階段中最耗時的。
  • 從新標記階段 阻塞用戶線程,從新標記併發標記階段裏新建立的對象,以及第二階段失去GCROOTS直接間接引用的垃圾對象,速度快,由於在併發標記階段變更的對象存在有記錄,只須要對變更的對象進行分析就能標記出來。
  • 併發清理 清理垃圾對象,不阻塞用戶線程,該階段也較爲耗時

最後根據-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompactio參數,選擇進行碎片整理,碎片整理的時候會阻塞用戶線程。

存在的問題:

  • 相比年輕代回收很消耗CPU資源,雖然併發標記階段和併發清理階段不阻塞用戶線程,可是很消耗CPU資源,系統吞吐量必然降低
  • 浮動垃圾-併發標記階段、併發清理階段進來的老年代對象,CMS會預留老年代空間,避免浮動垃圾產生致使老年代內存被撐爆,若是產生這種狀況,JVM會切換爲Serial Old回收器,阻塞用戶線程進行回收。

4.CMS回收分析

  • 爲何使用標記整理算法而不用複製算法?

我的以爲由於老年代預測存活對象會比年輕代多,若是用複製算法可能涉及到存活的對象會不少,意味着suprivor區也必須預留不少空間,這顯然是不合適的。

  • 爲何老年代回收效率比年輕代慢?

1.併發標記階段,存活對象多,須要追蹤對象(樹越深,越長)多,而MinorGC須要追蹤的對象少,速度很快

2.併發清理階段不是清理連續的內存空間,清理速度也慢。

3.碎片整理,對象也多,也慢。

G1回收機制

1.相關參數

  • -XX:USEG1GC 指定使用G1垃圾回收器
  • -XX:G1HeapRegionSize G1回收器指定regionSize,每一個region的大小,必須是2的倍數,默認是堆大小除以2048
  • -XX:NewSizePercent G1回收器指定新生代初始佔比(默認5%)
  • -XX:G1MaxNewSizePercent 新生代最大佔比(默認60%)
  • -XX:MaxGCPauseMills 指定G1回收器停頓時間,默認值200ms
  • -XX:InitiatingHeapOccupancyPercent 默認45,表示老年代佔據45%的region時,觸發混合回收
  • -XX:G1MixedGCCountTarget 混合回收階段執行多少次回收(默認8)
  • -XX:G1HeapWastePercent 默認5%,當空閒Region達到堆內存的5%時,中止混合回收,
  • -XX:G1MixedGCLiveThresholdPercent 默認85%,必須是存活對象低於85%的region才能夠回收

2.核心原理

  1. 內存中默認是把堆分紅多個region,沒有區分年輕代老年代具體必須是哪些region,而是由回收器自動進行管理。以下圖所示:

  1. 最核心的參數配置是-XX:MaxGCPauseMills,G1回收器會根據用戶設置的停頓時間(最大容許阻塞用戶線程的時間)以及每一個region的回收成本(G1會維護每一個region能夠回收的對象大小和回收預估時間),選擇性價比最高的region(回收對象大,回收預估時間小)來進行回收,儘可能在垃圾回收停頓時間內回收儘量多的垃圾。

  2. G1回收器不管是老年代和年輕代都是用複製清理算法。

  3. 針對大對象由單獨的region進行存放,不在進入老年代。

  4. 進入老年代的條件跟之前同樣,如達到必定年齡、動態年齡判斷規則、清理後的存活對象suprivor區放不下都會轉移到老年代。

3.回收觸發時機

minorGC的觸發時機

根據-XX:G1MaxNewSizePercent設置的新生代最大佔比(默認60%),達到的時候觸發minorGC。

mixedGC的觸發時機

G1回收器沒有單獨的oldGC,而是mixGC,其會回收老年代加年輕代以及大對象。其觸發條件是根據-XX:InitiatingHeapOccupancyPercent參數指定的(默認45%),表示老年代對象達到45%的region觸發mixedGC。

4.混合回收的四個階段

  • 初始標記階段 標記GC Roots直接引用的對象 阻塞用戶線程。
  • 併發標記階段 不阻塞用戶線程,最終GC Roots引用的全部存活對象
  • 最終標記階段 阻塞用戶線程,標記併發標記階段裏新增存活對象,取消標記原來標記的對象在併發標記階段變成垃圾的對象。
  • 混合回收階段 計算老年代中每一個region的存活對象數量,存活對象佔比,以及執行垃圾回收的預期性能和效率(阻塞用戶線程,但能夠分屢次間歇執行-由-XX:G1MixedGCCountTarget參數控制,默認分8次進行回收),接下來就是阻塞用戶線程,選擇部分region全力開始垃圾回收,G1會控制停頓時間在指定的範圍內。

注:

  • -XX:G1HeapWastePercent(默認5%)能夠指定空閒Region爲多少的時候中止混合回收。當達到的時候混合回收終止。
  • -XX:G1MixedGCLiveThresholdPercent能夠指定當一個region裏面的存活對象必須低於多少才能回收這個region(默認85%),表示必須是存活對象低於85%的region才能夠回收,爲何會有這個參數緣由在於存活對象大的region採用複製算法成本過高,移動成本以及空閒空間成本。
  • MixedGC發現沒有空閒的Region能夠承載本身的存活對象時(有可能設置的停頓時間過小,致使以前回收效果不大,再次回收時,在回收階段產生的垃圾沒有空閒region來存放,MixedGC失敗),JVM會採用單線程進行標記、清理和壓縮整理,該過程很是慢。

5.適用場景

  • 大內存機器,大內存就算是minorGC可能停頓時間也會很長。
  • 嚴格控制回收停頓的場景,注重用戶交互。

相比CMS+ParNew,二者都強調下降停頓時間,但G1更適合大內存機器,CMS+ParNew釋放內存比較簡單直接,會比G1更完全,佔用CPU也會更小。簡單來講大內存機器用G1,通常內存大小的機器建議用CMS+ParNew

相關文章
相關標籤/搜索