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