天天看點

JVM 垃圾回收器詳解

 小結:

  • 新生代    串行Serial            并行 Parallel(關注吞吐量)           并行ParNew
  • 老年代    串行 Serial Old     并行Parallel Old(關注吞吐量)     并發CMS(關注停頓時間)
  • 新生代:複制算法             老年代 :标記-整理算法(CMS:标記-清除,可以設定參數設定在每次Full GC  後者幾次Full GC後進行記憶體碎片的壓縮整理)
  • G1收集器基于“标記-整理”算法實作,也就是說不會産生記憶體碎片。G1收集器進行收集的範圍是個Java堆(包括新生代,老年代)。
  • 吞吐量:運作代碼時間/(運作代碼時間+GC收集時間)。GC時間縮短是以犧牲吞吐量和新生代空間來擷取的:系統把新生代調小一些,導緻垃圾收集發生得更頻繁一些,這樣停頓時間的确下降了,但是吞吐量也下降了。

上面有7中收集器,分為兩塊,上面為新生代收集器,下面是老年代收集器。如果兩個收集器之間存在連線,就說明它們可以搭配使用。

1, 串行回收器

1.1, 新生代串行回收器Serial

(1)特點: 

  –它僅僅使用單線程進行垃圾回收 

  –它是獨占式的垃圾回收 

  –進行垃圾回收時, Java應用程式中的線程都需要暫停(Stop-The-World) 

  –使用複制算法 

  –适合CPU等硬體不是很好的場合 

(2)設定參數: 

  -XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 當以client模式運作時, 它是預設的垃圾收集器

1.2, 老年代串行回收器Serial Old

  –同新生代串行回收器一樣, 單線程, 獨占式的垃圾回收器 使用标記-整理算法

  –通常老年代垃圾回收比新生代回收要更長時間, 是以可能會使應用程式停頓較長時間 

  -XX:+UseSerialGC 新生代, 老年代都使用串行回收器 

  -XX:+UseParNeGC 新生代使用ParNew回收器, 老年代使用串行回收器 

  -XX:+UseParallelGC 新生代使用ParallelGC回收器, 老年代使用串行回收器

2, 并行回收器

2.1, 新生代ParNew回收器

  –将串行回收多線程化, 

  –垃圾回收時, 應用程式仍會暫停, 隻不過由于是多線程回收, 在多核CPU上,回收效率會高于串行回收器, 反之在單核CPU, 效率會不如串行回收器 

  -XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器 

  -XX:+UseConcMarkSweepGC 新生代使用ParNew回收器, 老年代使用CMS回收器 

  -XX:ParallelGCThreads=n 指回ParNew回收器工作時的線程數量, cpu核數小時8時, 其值等于cpu數量, 高于8時,可以使用公式(3+((5*CPU_count)/8))

2.2, 新生代Parallel回收器

  –同ParNew回收器一樣, 不同的地方在于,它非常關注系統的吞吐量(通過參數控制) 

  –支援自适應的GC調節政策

(3)設定參數:

  -XX:+UseParallelGC  新生代用ParallelGC回收器, 老年代使用串行回收器 

  -XX:+UseParallelOldGC  新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系統吞吐量的控制: 

  -XX:MaxGCPauseMillis=n(機關ms)   設定垃圾回收的最大停頓時間, 

  -XX:GCTimeRatio=n(n在0-100之間)  設定吞吐量的大小, 假設值為n, 那系統将花費不超過1/(n+1)的時間用于垃圾回收 

  -XX:+UseAdaptiveSizePolicy  打開自适應GC政策, 在這種模式下, 新生代的大小, eden,survivior的比例, 晉升老年代的對象年齡等參數會被自動調整,以達到堆大小, 吞吐量, 停頓時間之間的平衡點

2.3, 老年代Parallel Old回收器

  –同新生代的ParallelGC回收器一樣, 是屬于老年代的關注吞吐量的多線程并發回收器 

  –使用标記壓縮算法, 

  -XX:+UseParallelOldGC  新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器, 是非常關注系統吞吐量的回收器組合, 适合用于對吞吐量要求較高的系統 

  -XX:ParallelGCThreads=n   指回ParNew回收器工作時的線程數量, cpu核數小時8時, 其值等于cpu數量, 高于8時, 可以使用公式(3+((5*CPU_count)/8))

3, 老年代CMS回收器(Concurrent Mark Sweep,并發、标記清除)

3.1, 老年代的并發回收器

JVM 垃圾回收器詳解
CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目标的收集器。CMS收集器是基于“标記-清除”算法實作的,整個收集過程大緻分為4個步驟:
①.初始标記(CMS initial mark)
②.并發标記(CMS concurrenr mark)
③.重新标記(CMS remark)
④.并發清除(CMS concurrent sweep)

     其中初始标記、重新标記這兩個步驟任然需要停頓其他使用者線程。初始标記僅僅隻是标記出GC ROOTS能直接關聯到的對象,速度很快,并發标記階段是進行GC ROOTS 根搜尋算法階段,會判定對象是否存活。而重新标記階段則是為了修正并發标記期間,因使用者程式繼續運作而導緻标記産生變動的那一部分對象的标記記錄,這個階段的停頓時間會被初始标記階段稍長,但比并發标記階段要短。

     由于整個過程中耗時最長的并發标記和并發清除過程中,收集器線程都可以與使用者線程一起工作,是以整體來說,CMS收集器的記憶體回收過程是與使用者線程一起并發執行的。

CMS收集器的優點:并發收集、低停頓,但是CMS還遠遠達不到完美,器主要有三個顯著缺點:

CMS收集器對CPU資源非常敏感。在并發階段,雖然不會導緻使用者線程停頓,但是會占用CPU資源而導緻引用程式變慢,總吞吐量下降。CMS預設啟動的回收線程數是:(CPU數量+3) / 4。

CMS收集器無法處理浮動垃圾,可能出現“Concurrent Mode Failure“,失敗後而導緻另一次Full  GC的産生。由于CMS并發清理階段使用者線程還在運作,伴随程式的運作自熱會有新的垃圾不斷産生,這一部分垃圾出現在标記過程之後,CMS無法在本次收集中處理它們,隻好留待下一次GC時将其清理掉。這一部分垃圾稱為“浮動垃圾”。也是由于在垃圾收集階段使用者線程還需要運作,

即需要預留足夠的記憶體空間給使用者線程使用,是以CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分記憶體空間提供并發收集時的程式運作使用。在預設設定下,CMS收集器在老年代使用了68%的空間時就會被激活,也可以通過參數-XX:CMSInitiatingOccupancyFraction的值來提供觸發百分比,以降低記憶體回收次數提高性能。要是CMS運作期間預留的記憶體無法滿足程式其他線程需要,就會出現“Concurrent Mode Failure”失敗,這時候虛拟機将啟動後備預案:臨時啟用Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。是以說參數-XX:CMSInitiatingOccupancyFraction設定的過高将會很容易導緻“Concurrent Mode Failure”失敗,性能反而降低。

最後一個缺點,CMS是基于“标記-清除”算法實作的收集器,使用“标記-清除”算法收集後,會産生大量碎片。空間碎片太多時,将會給對象配置設定帶來很多麻煩,比如說大對象,記憶體空間找不到連續的空間來配置設定不得不提前觸發一次Full  GC。為了解決這個問題,CMS收集器提供了一個-XX:UseCMSCompactAtFullCollection開關參數,用于在Full  GC之後增加一個碎片整理過程,還可通過-XX:CMSFullGCBeforeCompaction參數設定執行多少次不壓縮的Full  GC之後,跟着來一次碎片整理過程。

  –是并發回收, 非獨占式的回收器, 大部分時候應用程式不會停止運作 

  –針對年老代的回收器, 

  –使用并發标記清除算法, 是以回收後會有記憶體碎片, 可以使參數設定進行記憶體碎片的壓縮整理 

  –ParallelGC和ParallelOldGC主要關注吞吐量, CMS主要關注系統停頓時間 

(2)CMS主要步驟: 

  1. 初始标記 

  2. 并發标記 

  3. 重新标記 

  4. 并發清除

–>注:初始标記與重新标記是獨占系統資源的,不能與使用者線程一起執行,而其它階段則可以與使用者線程一起執行 

(3)設定參數: 

  -XX:-CMSPrecleaningEnabled  關閉預清理, 不進行預清理, 預設在并發标記後, 會有一個預清理的操作,可減少停頓時間 

  -XX:+UseConcMarkSweepGC  老年代使用CMS回收器, 新生代使用ParNew回收器 

  -XX:ConcGCThreads=n  設定并發線程數量, 

  -XX:ParallelCMSThreads=n  同上, 設定并發線程數量, 

  -XX:CMSInitiatingOccupancyFraction=n  指定老年代回收閥值, 即當老年代記憶體使用率達到這個值時, 會執行一次CMS回收,預設值為68, 設定技巧: (Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100)>=Xmn 

  -XX:+UseCMSCompactAtFullCollection  開啟記憶體碎片的整理, 即當CMS垃圾回收完成後, 進行一次記憶體碎片整理, 要注意記憶體碎片的整理并不是并發進行的, 是以可能會引起程式停頓 

  -XX:CMSFullGCsBeforeCompation=n  用于指定進行多少次CMS回收後, 再進行一次記憶體壓縮 

  -XX:+CMSParallelRemarkEnabled  在使用UseParNewGC 的情況下, 盡量減少 mark 的時間 

  -XX:+UseCMSInitiatingOccupancyOnly  表示隻有達到閥值時才進行CMS回收

3.2, Class的回收(永久區的回收)

設定參數: 

  -XX:+CMSClassUnloadingEnabled  開啟回收Perm區的記憶體, 預設情況下, 是需要觸發一次FullGC 

  -XX:CMSInitiatingPermOccupancyFraction=n  當永久區占用率達到這個n值時,啟動CMS回收, 需上一個參數開啟的情況下使用

4, G1回收器(jdk1.7後全新的回收器, 用于取代CMS)

  G1(Garbage First)收集器是JDK1.7提供的一個新收集器,G1收集器基于“标記-整理”算法實作,也就是說不會産生記憶體碎片。還有一個特點之前的收集器進行收集的範圍都是整個新生代或老年代,而G1将整個Java堆(包括新生代,老年代)。

  –獨特的垃圾回收政策, 屬于分代垃圾回收器, 

  –使用分區算法, 不要求eden, 年輕代或老年代的空間都連續 

  –并行性: 回收期間, 可由多個線程同時工作, 有效利用多核cpu資源 

  –并發性: 與應用程式可交替執行, 部分工作可以和應用程式同時執行, 

  –分代GC: 分代收集器, 同時兼顧年輕代和老年代 

  –空間整理: 回收過程中, 會進行适當對象移動, 減少空間碎片 

  –可預見性: G1可選取部分區域進行回收, 可以縮小回收範圍, 減少全局停頓 

(2)G1的收集過程 

1. 新生代GC: 

2. 并發标記周期: 

  –初始标記新生代GC(此時是并行, 應用程式會暫停止)–>根區域掃描–>并發标記–>重新标記(此時是并行, 應用程式會暫停止)–>獨占清理(此時應用程式會暫停止)–>并發清理 

3. 混合回收: 

  –這個階段即會執行正常的年輕代gc, 也會選取一些被标記的老年代區域進行回收, 同時處理新生代和年老輕 

4. 若需要, 會進行FullGC: 

  –混合GC時發生空間不足 

  –在新生代GC時, survivor區和老年代無法容納幸存對象時, 

  –以上兩者都會導緻一次FullGC産生 

  -XX:+UseG1GC  打開G1收集器開關, 

  -XX:MaxGCPauseMillis=n  指定目标的最大停頓時間,任何一次停頓時間超過這個值, G1就會嘗試調整新生代和老年代的比例, 調整堆大小, 調整晉升年齡 

  -XX:ParallelGCThreads=n  用于設定并行回收時, GC的工作線程數量 

  -XX:InitiatingHeapOccpancyPercent=n  指定整個堆的使用率達到多少時, 執行一次并發标記周期, 預設45, 過大會導緻并發标記周期遲遲不能啟動, 增加FullGC的可能, 過小會導緻GC頻繁, 會導緻應用程式性能有所下降

5, 其他GC相關的設定

5.1, System.gc()

(1)禁用System.gc() 

  -XX:+DisableExplicitGC  禁止程式中調用System.gc(), 加了此參數, 程式若有調用, 傳回的空函數調用 

   System.gc()的調用, 會使用FullGC的方式回收整個堆而會忽略CMS或G1等相關回收器 

(2)System.gc()使用并發回收 

  -XX:+ExplicitGCCinvokesConcurrent   使用并發方式處理顯示的gc, 即開啟後, System.gc()這種顯示GC才會并發的回收, (CMS, G1)

5.2, 并行GC前額外觸發的新生代GC

(1)使用并行回收器(UseParallelGC或者UseParallelOldGC)時, 會額外先觸發一個新生代GC, 目的是盡可能減少停頓時間 

(2)若不需要這種特性, 可以使用以下參數去除 

  -XX:-ScavengeBeforeFullGC   即去除在FullGC之前的那次新生代GC, 原本預設值為true 

  

5.3, 對象何時進入老年代

(1)當對象首次建立時, 會放在新生代的eden區, 若沒有GC的介入,會一直在eden區, GC後,是可能進入survivor區或者年老代 

(2)當對象年齡達到一定的大小 ,就會離開年輕代, 進入老年代, 對象進入老年代的事件稱為晉升, 而對象的年齡是由GC的次數決定的, 每一次GC,若對象沒有被回收, 則對象的年齡就會加1, 可以使用以下參數來控制新生代對象的最大年齡: 

  -XX:MaxTenuringThreshold=n  假設值為n , 則新生代的對象最多經曆n次GC, 就能晉升到老年代, 但這個必不是晉升的必要條件 

  -XX:TargetSurvivorRatio=n  用于設定Survivor區的目标使用率,即當survivor區GC後使用率超過這個值, 就可能會使用較小的年齡作為晉升年齡 

(3)除年齡外, 對象體積也會影響對象的晉升的, 若對象體積太大, 新生代無法容納這個對象, 則這個對象可能就會直接晉升至老年代, 可通過以下參數使用對象直接晉升至老年代的門檻值, 機關是byte 

  -XX:PretenureSizeThreshold  即對象的大小大于此值, 就會繞過新生代, 直接在老年代配置設定, 此參數隻對串行回收器以及ParNew回收有效, 而對ParallelGC回收器無效,

5.4, 在TLAB上配置設定對象(Thread Local Allocation Buffer, 線程本地配置設定緩存)

(1)TLAB: TLAB是一個線程專用的記憶體配置設定區域, 虛拟機為線程配置設定空間, 針對于體積不大的對象, 會優先使用TLAB, 這個可以加速對象的配置設定, TLAB是預設開啟的, 若要關閉可以使用以下參數關閉 

  -XX:-UseTLAB  關閉TLAB 

  -XX:+UseTLAB  開啟TLAB, 預設也是開啟的 

  -XX:+PrintTLAB  觀察TALB的使用情況 

  -XX:TLABRefillWasteFraction=n  設定一個比率n, 而refill_waste的值就是(TLAB_SIZE/n), 即TLAB空間較小, 大對象無法配置設定在TLAB,是以會直接配置設定到堆上,TLAB較小也很容易裝滿, 是以當TLAB的空間不夠配置設定一個新對象, 就會考慮廢棄目前TLAB空間還是直接配置設定到堆上, 就會使用此參數進行判斷, 小于refill_waste就允許廢棄, 而建立TLAB來配置設定對象,而大于refill_waste就直接在堆上配置設定, 預設是64 

  -XX:+ResizeTLAB  開啟TLAB自動調整大小, 預設是開啟的, 若要關閉把+号換成-号即可 

  -XX:TLABSize=n  設定一個TLAB的大小, 前提先關閉TLAB的自動調整

垃圾收集器參數總結

-XX:+<option> 啟用選項

-XX:-<option> 不啟用選項

-XX:<option>=<number> 

-XX:<option>=<string>

參數 描述
-XX:+UseSerialGC Jvm運作在Client模式下的預設值,打開此開關後,使用Serial + Serial Old的收集器組合進行記憶體回收
-XX:+UseParNewGC 打開此開關後,使用ParNew + Serial Old的收集器進行垃圾回收
-XX:+UseConcMarkSweepGC 使用ParNew + CMS +  Serial Old的收集器組合進行記憶體回收,Serial Old作為CMS出現“Concurrent Mode Failure”失敗後的後備收集器使用。
-XX:+UseParallelGC Jvm運作在Server模式下的預設值,打開此開關後,使用Parallel Scavenge +  Serial Old的收集器組合進行回收
-XX:+UseParallelOldGC 使用Parallel Scavenge +  Parallel Old的收集器組合進行回收
-XX:SurvivorRatio 新生代中Eden區域與Survivor區域的容量比值,預設為8,代表Eden:Subrvivor = 8:1
-XX:PretenureSizeThreshold 直接晉升到老年代對象的大小,設定這個參數後,大于這個參數的對象将直接在老年代配置設定
-XX:MaxTenuringThreshold 晉升到老年代的對象年齡,每次Minor GC之後,年齡就加1,當超過這個參數的值時進入老年代
-XX:UseAdaptiveSizePolicy 動态調整java堆中各個區域的大小以及進入老年代的年齡
-XX:+HandlePromotionFailure 是否允許新生代收集擔保,進行一次minor gc後, 另一塊Survivor空間不足時,将直接會在老年代中保留
-XX:ParallelGCThreads 設定并行GC進行記憶體回收的線程數
-XX:GCTimeRatio GC時間占總時間的比列,預設值為99,即允許1%的GC時間,僅在使用Parallel Scavenge 收集器時有效
-XX:MaxGCPauseMillis 設定GC的最大停頓時間,在Parallel Scavenge 收集器下有效
-XX:CMSInitiatingOccupancyFraction 設定CMS收集器在老年代空間被使用多少後出發垃圾收集,預設值為68%,僅在CMS收集器時有效,-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSCompactAtFullCollection 由于CMS收集器會産生碎片,此參數設定在垃圾收集器後是否需要一次記憶體碎片整理過程,僅在CMS收集器時有效
-XX:+CMSFullGCBeforeCompaction 設定CMS收集器在進行若幹次垃圾收集後再進行一次記憶體碎片整理過程,通常與UseCMSCompactAtFullCollection參數一起使用
-XX:+UseFastAccessorMethods 原始類型優化
-XX:+DisableExplicitGC 是否關閉手動System.gc
-XX:+CMSParallelRemarkEnabled 降低标記停頓
-XX:LargePageSizeInBytes 記憶體頁的大小不可設定過大,會影響Perm的大小,-XX:LargePageSizeInBytes=128m

Client、Server模式預設GC

新生代GC方式 老年代和持久代GC方式
Client Serial 串行GC Serial Old 串行GC
Server Parallel Scavenge  并行回收GC Parallel Old 并行GC

Sun/oracle JDK GC組合方式

Serial Old  并行GC
ParNew 并行GC

CMS 并發GC 

當出現“Concurrent Mode Failure”時

采用Serial Old 串行GC

轉自  http://blog.csdn.net/lghuntfor/article/details/51052737

 http://blog.csdn.net/java2000_wl/article/details/8030172

繼續閱讀