0050-垃圾回收器

1. 基礎定義

1.1 按線程數分

按線程數分,垃圾回收器可以分爲串行或者並行(指的是垃圾回收的線程)


1. 串行垃圾回收器

2. 並行垃圾回收器


在這裏插入圖片描述

1.2 按工作模式分

按工作模式劃分,可以分爲併發的垃圾回收器和獨佔式的垃圾回收器


1. 併發垃圾回收器

2. 獨佔式垃圾回收器


在這裏插入圖片描述

1.3 性能指標

1. 吞吐量

運行用戶代碼的時間佔總運行時間的比例(總運行時間=程序運行時間+內存回收時間)

2. 暫停時間

執行垃圾收集時,程序的工作線程被暫停的時間

3. 內存佔用

Java堆區所佔用的內存大小

這三者共同構成一個“不可能三角”,類似CAP理論,現在的追求標準,在最大吞吐量優先的情況下,降低停頓時間

2. 垃圾回收器概述

1. 串行或者並行

  1. 串行回收器:Serial、Serial Old

  2. 並行回收器:ParNew、Parallel Scavenge、Parallel Old

  3. 併發回收器:CMS、G1


    在這裏插入圖片描述

2. 新生代或老年代

  1. 新生代收集器:Serial、ParNew、Parallel Scavenge

  2. 老年代收集器:Serial Old、Parallel Old、CMS

  3. 整堆收集器:G1


    在這裏插入圖片描述

3. 組合關係


在這裏插入圖片描述

  1. 連線表示可以搭配使用

  2. 紅色虛線Serial+CMS、ParNew+Serial Old,在jdk8中標明爲廢棄,jdk9中移除這些組合

  3. 綠色虛線,Parallel Scavenge+Serial Old,jdk14中廢棄

  4. jdk1.7u4 G1可用

  5. jdk8默認的垃圾回收器組合Parallel Scavenge + Parallel Old

  6. jdk9默認的垃圾回收器爲G1

4. 查看當前虛擬機使用的垃圾回收器命令

  1. -XX:+PrintCommandLineFlags
-XX:InitialHeapSize=266122880 -XX:MaxHeapSize=4257966080 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
  1. 使用命令行指令:jinfo -flag 相關垃圾回收器參數 進程ID
C:\Users\Administrator>jps
23088 Launcher
23268 RemoteMavenServer
26520 Jps
12604
23788 LogUtilTest

C:\Users\Administrator>jinfo -flag UseParallelGC 23788
-XX:+UseParallelGC

3. Serial-串行回收器

3.1 Serial

1. 區域:新生代

2. 算法:複製算法

3. 特點:串行回收,STW

4. 回收圖示


在這裏插入圖片描述

5. 啓用參數


-XX:+UseSerialGC參數可以指定年輕代和老年代都是用串行回收器


等價於新生代用Serial GC且老年代用Serial Old GC

3.2 Serial Old

1. 區域:老年代

2. 算法:標記-壓縮算法

3. 其餘和Serial回收器一致

4. ParNew-並行回收器

1. 區域:新生代

2. 算法:複製算法

3. 特點:並行回收,STW

4. 回收圖示


在這裏插入圖片描述

5. 啓用參數


-XX:UseParNewGC:啓用垃圾回收器


-XX:ParallelGCThreads:線程並行線程的數量,默認開啓和CPU核數相同的線程數

5. Parallel-吞吐量優先回收器

5.1 Parallel Scavenge

1. 區域:新生代

2. 算法:複製算法

3. 特點:並行、STW

4. 與ParNew回收器的區別

Parallel Scavenge目標是達到可控制的吞吐量,同時擁有自適應調節策略

5. 回收圖示


在這裏插入圖片描述

6. 啓用參數

  • -XX:+UseParallelGC 手動指定年輕代使用Parallell垃圾回收器
  • -XX:+UseParallelOldGC 手動指定老年代使用Parallel垃圾回收器


    上述兩個開啓一個,另外一個自動開啓
  • -XX:ParallelGCThreads 設置垃圾回收並行收集的線程數


    默認情況下,當cpu核數小於8時,默認線程數等於cpu核數;當cpu核數大於8時,線程數等於3+[5*cpu核數/8]

5.2 Parallel Old

1. 區域:老年代

2. 算法:標記-壓縮算法

3. 特點:並行、STW

6. CMS-低延遲迴收器

1. 區域:老年代

2. 算法:標記-清除算法

3. 特點:併發、STW

4. 回收圖示


在這裏插入圖片描述

  • 初始標記(Initial-Mark):僅標記GC Roots能直接關聯到的對象,此過程需要STW
  • 併發標記(Concurrent-Mark)階段:從初始標記出的直接關聯對象,開始遍歷出整個對象圖,垃圾收集器線程和用戶線程併發運行
  • 重新標記(Remark):修正併發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄

重新標記,重新從GC Root開始查找新關聯的對象,並進行標記;而初始標記、併發標記兩個步驟標記的對象,即使併發標記過程中已經沒有相關引用了,也不會再去清除這些對象的標記(直到等到下一次GC發生的時候再去清除)

這一步需要重新確認所有存活的對象,因爲下一步需要清除垃圾了,如果沒有重新標記,那麼會清除掉併發標記過程中新產生的對象

  • 併發清除(Concurrent-Sweep):清理刪除標記階段已經死亡的對象,釋放內存

CMS爲什麼安全?

  1. 初始標記:沒有安全問題,標記出來的都是可達對象
  2. 併發標記:根據上一步標記的GC Roots直接對象獲取的對象圖,有可能會有部分對象在這個階段死亡(浮動垃圾,等待下一次垃圾回收),也有可能在這個階段新增一部分GC Roots
  3. 重新標記:重新獲取GC Root可達對象(不包括初始標記已經獲取的),1,2,3可以得到當前所有存活的對象(包括一小部分浮動垃圾),根據空閒列表也就可以得到當前所有不可達對象
  4. 併發清除:清除3中的不可達對象

5. 爲什麼採用標記-清除算法

併發清除的時候,要保證用戶線程的執行,同時這個時候不可達對象地址已經確定,因此可達對象不能修改地址,所以不能用標記-壓縮算法(需要在STW環境下使用)

6. 優點

  • 併發收集
  • 低延遲

7. 缺點

  • 會產生內存碎片,無法分配大對象,提前觸發Full GC
  • 無法清理浮動垃圾

8. 啓用參數

  • -XX:UseConcMarkSweepGC 開啓該參數後會自動將-XX:UseParNewGC打開,經ParNew(年輕代)+CMS(老年代)+Serial Old

  • -XX:CMSInitiatingOccupancyFraction=percent
    設置堆內存使用率的閾值,達到該閾值以後,開始回收。
    jdk5-68% jdk6以後92%
    如果內存增長緩慢可以設置較大的值,否者需要設置較低的值,降低Full Gc的次數

  • -XX:+UseCMSCompactAtFullCollection 指定在執行完Full GC後對內存空間進行壓縮整理,可以避免內存碎片的產生,停頓時間會變長

  • XX:CMSFullGCsBeforeCompaction 設置多少次Full GC後對內存進行壓縮

  • XX:ParallelCMSThreads 設置CMS的線程數量,默認(ParallelCMSThreads+3)/4

9. 說明
jdk9以後已經廢棄,jdk14以後已經刪除

7. G1-區域化分代式回收器

延遲可控的情況下獲得儘可能高的吞吐量
1. 區域:全堆

2. 算法:region之間是複製算法,整體上是標記-壓縮算法

3. 特點:重新調整了內存結構,還存在新生代和老年代,但是各自都不是連續的


原始的內存結構


在這裏插入圖片描述


現在的結構,每個格子稱爲一個Region


在這裏插入圖片描述

4. 可預測的時間模型


每個Region垃圾佔比稱爲回收價值,垃圾佔比越高回收價值越高


在給定的停頓時間N毫秒內,選取若干個高價值的Region進行回收,儘量將回收時間控制在N毫秒內

5. 回收過程

在這裏插入圖片描述


5.1 年輕代GC(Young GC)

  • 掃描根

根是static變量指向的對象,正在執行的方法調用鏈條上的局部變量。根引用連同Rset記錄的外部引用作爲掃描存活對象的入口

  • 更新Rset

處理dirty card queue中的card,更新Rset。此階段完成以後,Rset可以準確的反映老年代對所在的內存分段中對象的引用

  • 處理Rset

識別被老年代對象指向的Eden中的對象,這些被指向的Eden中的對象被認爲是存活的對象

  • 複製對象

此階段,對象樹被遍歷,Eden區內存存活的對象被複制到Survivor區中空的內存分段,Survivor區內存段中存活的對象如果年齡未達閾值,年齡加1,達到閾值會被複制到Survivor空的內存分段,如果Survivor空間不夠,Eden空間的部分數據會直接晉升到老年代空間

  • 處理引用

處理Soft,Weak,Phantom,Final,JNI Weak等引用

5.2 老年代併發標記過程(Concurrent Marking)

  • 初始標記階段

標記從根節點直接可達的對象,這個階段是STW的,並且會觸發一次年輕代的GC

  • 根區域掃描

G1 GC掃描Survivor區直接可達的老年代區域對象,並標記被引用的對象,這一過程必須再young GC之前完成

  • 併發標記

在整個堆中進行併發標記(和應用程序併發執行),此過程可能被young GC中斷。在併發標記階段,若發現區域對象中所有對象都是垃圾,那這個區域會被立即回收。同時,併發標記過程中,會計算每個區域的對象活性(區域中存活對象的比例)

併發標記帶來的問題

併發標記是和用戶線程一起運行的,在標記過程中,有可能會產生新的根對象(再次標記時需要修正),原來的存活對象也可能會消失(浮動垃圾)

  • 再次標記

修正併發標記的結果,時STW的,G1中採用了比CMS更快的初始快照算法

  • 獨佔清理

計算各個區域的存活對象和GC回收比例,並進行排序,識別可以混合回收的區域。爲下階段做鋪墊。是STW的(這個階段不會實際清理垃圾)

  • 併發清理

根據標記結果清理垃圾

5.3 混合回收(Mixed GC)


混合回收不等於Full GC;混合回收會回收整個Young Region同時也會回收一部分Old Region

6. Full GC


6.1 觸發原因

  • Survivor複製存活對象到Old區的時候沒有空的內存分段可用
  • 併發處理過程完成之前空間耗盡

6.2 Full GC處理方式


停止所有的應用程序,使用單線程的內存回收算法,進行垃圾回收

7. 使用方式

  1. 開啓G1垃圾收集器(jdk1.7u4 G1後可用,jdk1.9以後默認爲此收集器)
  2. 設置堆的最大內存
  3. 設置最大停頓時間

8. 常用參數設置

  • -XX:+UseG1GC 手動指定G1垃圾收集器執行內存回收任務
  • XX:MaxGCPauseMillis 設置期望達到的最大GC停頓時間指標,默認是200ms
  • -XX:InitiatingHeapOccupancyPercent=percent 設置觸發GC週期的java堆佔用率的閾值,超過此值,就觸發GC,默認值時45

8. GC總結

截至jdk1.8的垃圾回收器


在這裏插入圖片描述


發展階段


在這裏插入圖片描述

9. GC日誌分析

1. 日誌參數配置

  • -XX:+PrintGC 輸出gc日誌 類似-verbose:gc
  • -XX:+PrintGCDetails 輸出gc的詳細日誌
  • -XX:+PrintGCTimeStamps 輸入gc的時間戳(以基準時間的形式)
  • -XX:+PrintGCDateStamps 輸出gc的時間戳(以日期的格式)
  • XX:+PrintHeapAtGC 在進行GC的前後打印出堆的信息
  • -Xloggc:…/logs/gc.log 日誌文件的輸出路徑

2. 回收日誌


年輕代gc日誌


在這裏插入圖片描述


Full GC


在這裏插入圖片描述


備註

  • GC和Full GC說明停頓類型,Full GC會發生較長時間的STW,應該儘量減少
  • Searial(Default New Generation)顯示DefNew
  • ParNew(Parallel New Generation)顯示ParNew
  • Parallel Scavenge顯示PSYoungGen
  • G1顯示garbage-first heap

GC日誌可視化分析工具

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