第一周JVM核心技術-工具與GC策略

一、 JDK工具

1.1 內置命令行工具

工具 簡介
jps/jinfo 查看java進程
jstat 查看JVM內部GC資訊
jmap 查看JVM堆或類佔用空間資訊
jstack 查看執行緒資訊
jcmd 整合性的命令
jrunscript/jjs 執行js命令

1. jps

查看運行的java進程

jps

查看詳細的java進程資訊

jps -mlv

2. jstat

查看gc詳細資訊

jstat -gc pid 1000 100

pid可以根據jps命令得到,參數里的1000代表每1000毫秒統計一次,100代表統計100次

查看gc相關區域的使用率

jstat -gcutil pid 1000 100

3. jmap

查看堆記憶體

jmap -heap pid
jmap -histo pid
jmap -dump:format=b,file=xx.hprof
pid

4. jstack

查看執行緒的情況

jstack -l pid

5. jcmd

綜合命令,能查看堆、執行緒等情況

jcmd pid VM.version
jcmd pid VM.flags
jcmd pid VM.command_line
jcmd pid VM.system_properties
jcmd pid Thread.print
jcmd pid GC.class_histogram
jcmd pid GC.heap_info

6. jrunscript/jjs

可以運行js腳本

jrunscript -e “print(‘abc’)”
jrunscript -l js -f test.js

1.2 JDK內置圖形化工具

可自行下載了解

  1. jconsole
  2. jvisualvm
  3. VisualGC
  4. jmc

二、GC的背景與原理

2.1 識別存活對象

怎麼判斷對象沒有引用了呢?

引用計數 -> 引用跟蹤

引用計數:
引用計數的方法,無法循環調用的情況。如A調用B,B調用C,C調用A。這樣他的引用計數永遠不會為0,也就不能被回收。

引用跟蹤:
根據一些GC ROOT對象,向下尋找他們引用的對象。

可以作為GC ROOT的對象:

  • 當前正在執行的方法里的局部變數和輸入參數
  • 活動執行緒
  • 所有類的靜態欄位
  • JNI引用

2.2 GC演算法

  1. 清除演算法

標記 – 清除

  1. 複製演算法

標記 – 複製

  1. 整理演算法

標記 – 清除 – 整理

2.3 垃圾回收器

1. Serial GC /ParNew GC

-XX:+UseSerialGC 配置串列 GC

Serial GC,也叫串列GC。年輕代使用複製演算法,老年代使用整理演算法。兩者都是單執行緒的垃圾收集器,所以整個過程都是Stop-The-World。這種垃圾回收器適用於幾百MB的堆記憶體,並且單核CPU時比較有用。

ParNew GC是Serial GC的多執行緒版本,一般是配合CMS使用。

2. Parallel GC

-XX:+UseParallelGC -XX:+UseParallelOldGC

也叫並行GC,年輕代使用複製演算法,老年代使用整理演算法。可以通過
-XX:ParallelGCThreads=N 來指定 GC 執行緒數, 其默認值為 CPU 核心數

並行GC適用於多核伺服器,主要目標是增加吞吐量。

  • 在GC期間,所有CPU內核都在並發清理垃圾,所以總暫停時間會更短
  • 在兩次GC周期的間隔期,沒有GC執行緒在運行,不會消耗任何執行緒資源
3. CMS

-XX:+UseConcMarkSweepGC

採用的標記清除演算法。CMS的設計目標是避免長時間的卡頓。主要通過兩種手段達成目標

  • 不對老年代進行整理,而是使用空閑列表管理記憶體空間的回收
  • 在標記-清除階段的大部分工作和應用執行緒是一起並發執行的。

CMS清理的六個階段

  • 初始標記 標記根對象和根對象直接引用的對象
  • 並發標記 從前一階段找到的根對象算起,標記所有存活對象
  • 並發預清理 因為是並發執行的,所以過程中可能有一些引用發生改變,JVM會通過Card的方式需要標記一下,這就是所謂的卡片標記
  • 最終標記 本階段的目標是完成所有對象的標記。
  • 並發清除 刪除不再使用的對象
  • 並發重置 重置CMS演算法的內部數據,為下一次GC循環做準備。
4. G1 GC

-XX:+UseG1GC -XX:MaxGCPauseMillis=50

G1的全稱是Garbage-First,意為垃圾優先,哪一塊垃圾最多就優先清理哪一塊。

G1的設計目標是將STW的時間變成可預期且可配置的。

他不將堆分為年輕代和老年代,而是劃分為多個(通常是2048)可以存放對象的小塊區域.每個小塊,可能一會被定義為Eden區,一會被定義為老年代。

這樣劃分之後,G1就可以不必每次都去收集整個堆空間,而是以增量的方式處理。

G1清理的階段

  • 初始標記 此階段標記所有從GC根對象直接可達的對象。
  • Root區掃描 標記從根區域可達的對象
  • 並發標記
  • 再次標記
  • 清理

-XX:G1NewSizePercent:初始年輕代占整個 Java Heap 的大小,默認值為 5%;

-XX:G1MaxNewSizePercent:最大年輕代占整個 Java Heap 的大小,默認值為 60%;

-XX:G1HeapRegionSize:設置每個 Region 的大小,單位 MB,需要為 1,2,4,8,16,32 中的某個值,默 認是堆記憶體的 1/2000。如果這個值設置比較大,那麼大對象就可以進入 Region 了。

-XX:ConcGCThreads:與 Java 應用一起執行的 GC 執行緒數量,默認是 Java 執行緒的 1/4,減少這個參數的數值可 能會提升並行回收的效率,提高系統內部吞吐量。如果這個數值過低,參與回收垃圾的執行緒不足,也會導致並行回收機制耗時加長。

-XX:+InitiatingHeapOccupancyPercent(簡稱 IHOP):G1 內部並行回收循環啟動的閾值,默認為 Java Heap 的45%。這個可以理解為老年代使用大於等於 45% 的時候,JVM會啟動垃圾回收。這個值非常重要,它決定了在什麼時間啟動老年代的並行回收。

-XX:G1HeapWastePercent:G1停止回收的最小記憶體大小,默認是堆大小的 5%。GC 會收集所有的Region 中 的對象,但是如果下降到了5%,就會停下來不再收集了。就是說,不必每次回收就把所有的垃圾都處理完,可以遺留少量的下次處理,這樣也降低了單次消耗的時間

-XX:+GCTimeRatio:這個參數就是計算花在 Java 應用執行緒上和花在 GC 執行緒上的時間比率,默認是 9,跟新生代記憶體的分
配比例一致。這個參數主要的目的是讓用戶可以控制花在應用上的時間,G1的計算公式是100/(1+GCTimeRatio)。這樣如果參數設置為 9,則最多 10% 的時間會花在 GC工作上面。ParallelGC的默認值是 99,表示 1% 的時間被用在 GC 上面,
這是因為 Parallel GC 貫穿整個 GC,而 G1 則根據 Region來進行劃分,不需要全局性掃描整個記憶體堆。

-XX:+UseStringDeduplication:手動開啟 Java String 對象的去重工作,這個是 JDK8u20 版本之後新增的參數,主要用於
相同 String 避免重複申請記憶體,節約 Region 的使用。

-XX:MaxGCPauseMills:預期 G1 每次執行 GC 操作的暫停時間,單位是毫秒,默認值是 200 毫秒,G1 會盡量保證控制在
這個範圍內

5. ZGC

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx16g

主要特點:

  1. GC最大停頓時間不超過10ms
  2. 堆記憶體支援區域廣,小到幾百M,大到4TB
  3. 與G1相比,應用吞吐量有下降,但不超過15%
  4. JDk15前僅支援Linux系統

通過著色指針和讀屏障,實現幾乎全部的並發執行,幾毫秒級別的延遲,線性可擴展;

多個垃圾回收器對比

GC如何選擇?

選擇正確的GC演算法,唯一可行的方式就是實踐,一般性的指導準則是:

  1. 如果考慮吞吐量優先,CPU資源最大程度的處理業務,用Parallel GC
  2. 如果考慮系統低延遲,則用CMS
  3. 如果系統堆記憶體較大,同時希望平均GC時間可控,使用G1 GC。一般超過4G,算是比較大,超過8G ,比如16G-64G,非常推薦G1 GC

各版本默認垃圾回收器

版本 垃圾回收器
JDK1.7 Parallel
JDK1.8 Parallel
JDK1.9 G1