第一周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內置圖形化工具
可自行下載了解
- jconsole
- jvisualvm
- VisualGC
- jmc
二、GC的背景與原理
2.1 識別存活對象
怎麼判斷對象沒有引用了呢?
引用計數 -> 引用跟蹤
引用計數:
引用計數的方法,無法循環調用的情況。如A調用B,B調用C,C調用A。這樣他的引用計數永遠不會為0,也就不能被回收。
引用跟蹤:
根據一些GC ROOT對象,向下尋找他們引用的對象。
可以作為GC ROOT的對象:
- 當前正在執行的方法里的局部變數和輸入參數
- 活動執行緒
- 所有類的靜態欄位
- JNI引用
2.2 GC演算法
- 清除演算法
標記 – 清除
- 複製演算法
標記 – 複製
- 整理演算法
標記 – 清除 – 整理
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
主要特點:
- GC最大停頓時間不超過10ms
- 堆記憶體支援區域廣,小到幾百M,大到4TB
- 與G1相比,應用吞吐量有下降,但不超過15%
- JDk15前僅支援Linux系統
通過著色指針和讀屏障,實現幾乎全部的並發執行,幾毫秒級別的延遲,線性可擴展;
多個垃圾回收器對比
GC如何選擇?
選擇正確的GC演算法,唯一可行的方式就是實踐,一般性的指導準則是:
- 如果考慮吞吐量優先,CPU資源最大程度的處理業務,用Parallel GC
- 如果考慮系統低延遲,則用CMS
- 如果系統堆記憶體較大,同時希望平均GC時間可控,使用G1 GC。一般超過4G,算是比較大,超過8G ,比如16G-64G,非常推薦G1 GC
各版本默認垃圾回收器
版本 | 垃圾回收器 |
---|---|
JDK1.7 | Parallel |
JDK1.8 | Parallel |
JDK1.9 | G1 |