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

女友在高考發表於2021-05-16

一、 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

相關文章