(三)學習JVM —— 垃圾回收器

(一)學習JVM ——運行時數據區域 算法

(二)學習JVM —— 垃圾回收機制 安全

(三)學習JVM —— 垃圾回收器多線程

(四)學習JVM —— 內存分配與回收策略 併發

垃圾回收器

如圖所示,常見的垃圾回收器有七種。性能

三種新生代回收器,三種老年代回收器,搭配起來可管理整個GC堆,具體以下(新生代回收器在前,老年代回收器在後):學習

  • Serial + Serial Old;
  • Serial + CMS;
  • ParNew + Serial Old;
  • ParNew + CMS;
  • Parallel Scavenge + Serial Old;
  • Parallel Scavenge + Parallel Old;

還有一種全可兼顧的回收器G1。網站

下面對每種垃圾回收器作逐一進行介紹,並在最後對垃圾回收器的參數進行一個總結。spa

新生代回收器

Serial 回收器

Serial 回收器是一個單線程的收集器,它進行垃圾回收時,必須暫停其餘全部的工做線程(Stop The World),直到它收集結束。.net

途中灰色豎線就是安全點,然後進入STW,STW後由一條GC單線程作垃圾回收,而後恢復STW。線程

Serial的優勢是它的簡單和高效,對於限定單個CPU的環境來講,Serial收集器因爲沒有線程交互的開銷,專心作垃圾收集天然能夠得到最高的單線程收集效率。

Serial收集器對運行在Client模式下的虛擬機來講是一個很好的選擇,好比用戶的桌面應用等。

ParNew 回收器

ParNew回收器是Serial回收器的多線程版本。除了使用多條線程回收垃圾外,沒有什麼不一樣,進入STW後,由多條線程進行垃圾回收。它在Server模式下的虛擬機是首選。

可是在單CPU環境下,ParNew毫不會作的比Serial更好,甚至因爲線程之間存在交互開銷,都不能保證百分百能夠超越Serial。

可使用-XX:ParallelGCThreads參數限制垃圾回收的線程數。

Parallel Scavenge 回收器

Parallel Scavenge是一個新生代的收集器,它的關注點與其餘收集器不一樣,其餘收集器立志於所點垃圾回收時用戶的等待時間,而Parallel Scavenge的目的是達到一個可控制的吞吐量(Throughput)。

所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值。

吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)

例如,虛擬機運行是100分鐘,其中垃圾回收花掉1分鐘,那吞吐量就是99%了。

Parallel Scavenge提供了連個參數用於精確控制吞吐量,分別是控制最大垃圾回收停頓時間的-XX:MaxGCPauseMillis參數以及直接設置吞吐量大小的-XX:GCTimeRatio參數。

-XX:MaxGCPauseMillis參數須要一個大於0的值,垃圾回收器將盡量地保證內存回收花費的時間不超過設定值,不過不要認爲把該值設置小一點就能讓垃圾收集速度變得更快,Parallel Scavenge GC停頓時間縮短是以犧牲吞吐量和新生代空間換取的,時間越短,收集次數越頻繁,吞吐量越低。

-XX:GCTimeRatio參數的值須要一個大於0且小於100的整數,就是垃圾回收時間佔總時間的比率,至關因而吞吐量的倒數。若是把數字設置成19,則垃圾回收時間佔總時間的5% = 1 / (1 + 19)。

還有一個值得一提的參數,-XX:UseAdaptiveSizePolicy。這是一個開關參數,開啓後虛擬機會根據當前系統的運行狀況收集性能監控信息。不須要手動制定新生代大小(-Xmn)、Eden與Survivor的比例(-XX:SurvivorRatio)、晉升老年代的對象大小(-XX:PretenureSizeThreshold)等細節,動態調整這些參數以提供最合適的停頓時間和吞吐量,這種調節方式被成爲GC自適應的調節策略(GC Ergonomics)。

老年代回收器

Serial Old 回收器

Serial Old 是 Serial 的老年代版本,它一樣是一個單線程收集器,使用標記 & 整理算法。這個回收器主要也是在Client模式下使用,還有一個用途是給CMS回收器作後背預案。

Parallel Old 回收器

Parallel Old 是 Parallel Scavenge 收集器的老年代版本,使用多線程和標記 & 整理算法。

CMS 回收器

CMS ( Concurrent Mark Sweep ) 回收器是一種以獲取最短回收停頓時間爲目標的回收器。目前很大一部分的Java應用集中在互聯網網站或B/S系統的服務端上,這類應用尤爲重視服務的響應速度,但願系統停頓時間最短,以給用戶帶來較好的體驗。

CMS是基於標記 & 清除算法實現的,整個過程分4個步驟:

  1. 初始標記 ( CMS initial mark );
  2. 併發標記 ( CMS concurrent mark );
  3. 從新標記 ( CMS remark );
  4. 併發清除 ( CMS concurrent sweep );

其中,初始標記、從新標記這兩個階段須要STW。

初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快。併發標記階段就是進行GC Roots Tracing的過程,而從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間會比初始標記階段稍長一些,但遠比並發標記時間短。

使用CMS還須要瞭解它的不足之處,有如下3點不足:

CMS對CPU資源很是敏感。CMS默認開啓回收線程數是 ( CPU數量 + 3) / 4,在4個CPU以上時,併發回收線程很多於25%的CPU資源,但隨着CPU數量降低(譬以下降到2個),CMS對用戶程序的影響就很大,在執行用戶程序時還要分出50%的執行速度。

CMS收集器沒法處理浮動垃圾 (Floating Garbage),可能出現"Concurrent Mode Failure"失敗致使另外一次Full GC的產生。因爲CMS併發清理借點,用戶線程還在繼續運行,就會有新垃圾不斷產生,這些垃圾就被稱爲浮動垃圾。也是因爲用戶線程還在繼續,就須要給用戶線程預留足夠的內存空間供其使用,所以在JDK1.5時期,CMS當老年代使用了68%的空間後就會被激活進行回收,JDK1.6時這個閥值提升到了92%。另外,CMS還提供了參數-XX:CMSInitiatingOccupancyFraction來設置閥值,可是設置的過高容易致使大量的"Concurrent Mode Failure",出現時,CMS會啓動後背預案,調用Serial Old進行一次回收。

收集結束時會有大量空間碎片產生。空間碎片過多時,將沒有連續空間給大對象分配,每每會出現老年代還有很大剩餘空間,可是沒法找到足夠大的連續空間來分配當前對象,不得不提早觸發一次Full GC。爲了解決這個問題,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開關參數,在CMS頂不住要進行FullGC時,會開啓內存碎片的合併整理過程,內存整理的過程是沒法併發的,因此時間會變長。另外,CMS還提供了一個參數-XX:CMSFullGCsBeforeCOmpaction,這個參數用於設置執行多少次不壓縮的FullGC後,跟着來一次帶壓縮的FullGC(默認值爲0,每次都壓縮)。

G1 回收器

G1 (Garbage-First) 收集器是當今收集器技術,是一款面向服務端應用的垃圾回收器,它有以下特色:

  • 分代回收:G1不須要與其餘回收器配合就能獨立管理整個GC堆;
  • 可預測的停頓:G1除了最求低停頓外,還能創建可預測的停頓時間模型;
  • 並行與併發:G1能充分利用多CPU、多核環境下的硬件優點來縮短STW;
  • 空間整合:G1採用標記&整理算法實現垃圾回收;

分代回收

G1將整個堆劃分爲多個大小相等的獨立區域 (Region),雖然還保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔離了,他們都是一部分Region的集合。

可預測的停頓

G1之因此能創建可預測的停頓時間模型,是由於它能夠有計劃地避免在整個堆中進行全區域的垃圾回收。G1根據各個Region裏面的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region。

在G1中,Region之間的對象引用以及其餘回收器中的新生代與老年代之間的對象引用,都是使用Remembered Set來避免全堆掃描的,G1中每一個Region都有一個與之對應的Remembered Set,虛擬機發現程序在對Reference類型的數據進行寫操做時,會產生一個Write Barrier暫時中斷寫操做,檢查Reference引用的對象是否在其餘Region之中,若是在,便經過CardTable把相關引用信息記錄到被引用對象所屬的Region的Remembered Set之中。當進行內存回收時,在GC根節點的枚舉範圍中加入Remembered Set便可保證不對全堆掃描頁不會有遺漏。

並行與併發

若是不計算維護RememBered Set的操做,G1運做大體可分爲如下幾個步驟:

  1. 初始標記 (Initial Marking):僅僅只是標記一下GC Roots能直接關聯到的對象,而且修改TAMS (Next Top at Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這個階段有一個很短的STW;
  2. 併發標記 (Concurrent Marking):是從GC Roots開始對堆中的對象進行可達性分析,找出存活的對象,這階段耗時很長,可與用戶程序併發執行。
  3. 最終標記 (Final Marking):該階段是爲了修正在併發標記階段因用戶程序繼續運行而致使標記產生變化的那一部分標記記錄,虛擬機將這段時間對象變化的記錄在線程Remembered Set Logs裏面,而後合併到Remembered Set中,這階段要STW,可是可並行執行;
  4. 篩選回收 (Live Data Counting and Evacuation):該階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的GC停頓時間來制定回收計劃;

垃圾回收器參數總結

垃圾回收相關的經常使用參數

UseSerialGC 虛擬機運行在Client模式下的默認值,使用Serial+Serial Old組合;
UseParNewGC 打開此開關後,使用ParNew + Serial Old組合;
UseConcMarkSweepGC 打開此開關後,使用ParNew + CMS + Serial Old組合;
UseParallelGC 虛擬機運行在Server模式下的默認值,使用Parallel Scavenge + Serial Old組合;
UseParallelOldGC 打開此開關後,使用Parallel Scavenge + Parallel Old組合;
SurvivorRatio 新生代中Eden區域和Survivor區域的容量比值,默認爲8,表明8:1:1;
PretenureSizeThreshold 直接晉升老年代的對象大小,設置這個參數後,大於這個參數的對象將直接在老年代分配;
MaxTenuringThreshold 晉升到老年代的對象年齡,通過一次MinorGC以後,年齡+1,超過這個值時進入老年代;
UseAdaptiveSizePolicy 動態調整堆中各個區域的大小以及進入老年代的年齡;
HandlePromotionFailure 是否容許分配擔保失敗,即老年代剩餘空間不足應付新生代的整個區域全部對象都存活的極端狀況;
ParallelGCThreads 設置並行GC時進行內存回收的線程數;
GCTimeRatio GC時間佔總時間的比率,默認爲99,容許1%的GC時間,僅在使用Parallel Scavenge收集器時生效;
MaxGCPauseMillis 設置GC最大停頓時間,僅在使用Parallel Scavenge收集器時生效;
CMSInitiatingOccupancyFraction 設置CMS收集器在老年代空間被使用多少後出發垃圾回收,僅在使用CMS收集器時生效;
UseCMSCompactAtFullCollection 設置CMS在完成垃圾回收後是否要進行一次內存碎片整理,僅在使用CMS收集器時生效;
CMSFullGCsBeforeCompaction 設置CMS在完成多少次垃圾回收後啓動一次內存碎片整理,僅在使用CMS收集器時生效;

(一)學習JVM ——運行時數據區域

(二)學習JVM —— 垃圾回收機制

(三)學習JVM —— 垃圾回收器

(四)學習JVM —— 內存分配與回收策略

相關文章
相關標籤/搜索