本文將穿透式解析JVM垃圾回收核心算法,涵蓋7大基礎算法+4大現代GC實現+3種內存分配策略,通過15張動態示意圖+GC日志實戰分析,帶您徹底掌握JVM內存自動管理機制。
一、GC核心概念體系
1.1 對象存亡判定法則
引用計數法致命缺陷:
// 循環引用導致內存泄漏
class Node {Node next;public static void main(String[] args) {Node a = new Node();Node b = new Node();a.next = b; // a引用計數=1b.next = a; // b引用計數=1a = null; // a引用計數=1(b.next仍引用)b = null; // b引用計數=1(a.next仍引用)// 兩個對象永遠無法回收!}
}
1.2 對象引用強度分級
引用類型 | 特點 | 回收條件 | 典型應用 |
---|---|---|---|
強引用 | Object obj = new Object() | 永不回收 | 普通對象 |
軟引用 | SoftReference<T> | 內存不足時回收 | 緩存系統 |
弱引用 | WeakReference<T> | 下次GC必回收 | WeakHashMap |
虛引用 | PhantomReference<T> | 對象回收跟蹤 | DirectByteBuffer清理 |
二、基礎垃圾回收算法詳解
2.1 標記-清除(Mark-Sweep)
執行流程:
-
標記階段:遍歷GC Roots標記存活對象
-
清除階段:回收未標記對象內存
內存布局變化:
初始狀態: [A][B][C][D] # 4個對象
標記后: [A*][B][C*][D] # *表示存活
清除后: [A][ ][C][ ] # 產生內存碎片
致命缺陷:
-
內存碎片化:導致大對象分配失敗
-
執行效率低:兩次堆遍歷(O(n)復雜度)
2.2 復制算法(Copying)
內存劃分:
Eden: [ 新生對象分配區 ]
Survivor0: [ 存活區 ]
Survivor1: [ 存活區 ]
執行過程:
HotSpot實現細節:
-
默認比例:Eden:Survivor?= 8:1:1
-
對象年齡計數器:-XX:MaxTenuringThreshold=15
-
分配擔保機制:-XX:HandlePromotionFailure
2.3 標記-整理(Mark-Compact)
執行步驟:
-
標記存活對象(同標記-清除)
-
所有存活對象向內存一端移動
-
清理邊界外內存
內存布局變化:
標記前: [A][ ][B][C][ ][D]
標記后: [A*][ ][B*][C*][ ][D*]
整理后: [A][B][C][D][ ][ ] # 消除碎片
優勢:避免內存碎片
代價:移動對象需更新引用(STW時間更長)
2.4 分代收集理論
核心假設:
-
弱分代假說:絕大多數對象朝生夕死
-
強分代假說:熬過多次GC的對象更難消亡
-
跨代引用假說:跨代引用是極少數
分代布局:
三、現代GC算法實現
3.1 CMS(Concurrent Mark Sweep)
四階段執行流程:
致命缺陷:
-
內存碎片:使用標記-清除算法
# 解決方案:開啟內存整理 -XX:+UseCMSCompactAtFullCollection
-
并發模式失敗:老年代空間不足
# 調優參數 -XX:CMSInitiatingOccupancyFraction=70 # 老年代70%時觸發 -XX:+UseCMSInitiatingOccupancyOnly
-
浮動垃圾:并發清理階段新產生的垃圾
3.2 G1(Garbage-First)
革命性設計:
核心流程:
調優關鍵參數:
-XX:G1HeapRegionSize=4m # Region大小
-XX:MaxGCPauseMillis=200 # 目標暫停時間
-XX:InitiatingHeapOccupancyPercent=45 # 觸發并發標記的堆使用率
3.3 ZGC(Z Garbage Collector)
三大黑科技:
-
染色指針(Colored Pointers)
// 64位指針結構 | 18位保留 | 1位Finalizable | 1位Remap | 1位Marked1 | 1位Marked0 | 42位地址 |
-
內存多重映射
# 不同視圖映射同一物理內存 $ cat /proc/<pid>/maps | grep heap
-
并發對象轉移
// 對象移動時通過指針自愈機制更新引用
執行階段:
3.4 Shenandoah GC
創新點:
-
Brooks指針:每個對象頭含轉發指針
struct Object {void* forward_ptr; // 指向新位置// ...其他字段 }
-
并發壓縮算法:無需STW完成堆壓縮
性能對比:
GC名稱 | 最大暫停時間 | 吞吐量損失 | JDK支持 |
---|---|---|---|
CMS | 100-500ms | 10-20% | ≤JDK14 |
G1 | 50-200ms | <15% | ≥JDK9 |
ZGC | <1ms | <15% | ≥JDK15 |
Shenandoah | <1ms | <10% | OpenJDK |
四、GC算法實戰分析
4.1 內存分配策略
對象優先Eden分配:
// -XX:+PrintGCDetails 日志
public class Allocation {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] a1 = new byte[2 * _1MB]; // 分配在Edenbyte[] a2 = new byte[2 * _1MB]; // 分配在Edenbyte[] a3 = new byte[2 * _1MB]; // 觸發Minor GC}
}
大對象直接進老年代:
-XX:PretenureSizeThreshold=3145728 # 3MB以上對象直接進老年代
長期存活對象晉升:
// -XX:MaxTenuringThreshold=1
public class TenuringThreshold {public static void main(String[] args) {byte[] a1 = new byte[_1MB / 4];byte[] a2 = new byte[4 * _1MB];byte[] a3 = new byte[4 * _1MB]; // 觸發Minor GCa3 = null;a3 = new byte[4 * _1MB]; // 再次觸發Minor GC}
}
4.2 GC日志深度解讀
CMS日志分析:
[GC (Allocation Failure) [ParNew: 367616K->40960K(367616K), 0.0468480 secs]-> 新生代回收,暫停46ms
[GC (CMS Initial Mark) [1 CMS-initial-mark: 524289K(786432K)] -> 初始標記階段
[CMS-concurrent-mark: 1.234/1.543 secs] -> 并發標記耗時1.543秒
[GC (CMS Final Remark) [YG occupancy: 275342 K (367616 K)]-> 重新標記階段
[CMS-concurrent-sweep: 0.876/0.987 secs]-> 并發清除耗時0.987秒
G1日志關鍵指標:
[GC pause (G1 Evacuation Pause) (young), 0.0234159 secs][Parallel Time: 22.3 ms][Ext Root Scanning: 3.5 ms][Update RS: 0.2 ms][Processed Buffers: 43][Scan RS: 1.8 ms][Code Root Scanning: 0.7 ms][Object Copy: 15.1 ms] # 對象復制耗時[Eden: 2048.0M(2048.0M)->0.0B(2048.0M) Survivors: 0.0B->102.0M Heap: 4096.0M(8192.0M)->2054.0M(8192.0M)]
五、高級調優技巧
5.1 避免Full GC的黃金法則
-
老年代空間擔保:
-XX:-HandlePromotionFailure # JDK6后已失效
-
元空間監控:
jstat -gcmetacapacity <pid> # 查看元空間使用
-
堆外內存控制:
-XX:MaxDirectMemorySize=128m
5.2 GC選擇策略矩陣
應用場景 | 推薦GC | 參數配置 |
---|---|---|
小型應用 | Serial | -XX:+UseSerialGC |
響應優先Web服務 | G1 | -XX:+UseG1GC -MaxGCPauseMillis=100 |
大內存服務 | ZGC/Shenandoah | -XX:+UseZGC -Xmx32g |
低延遲交易系統 | ZGC | -XX:+UseZGC -XX:SoftMaxHeapSize=4g |
六、常見生產問題解決方案
6.1 頻繁Full GC排查
診斷步驟:
-
jstat -gcutil <pid> 1000
?觀察內存趨勢 -
jmap -histo:live <pid>
?查看對象直方圖 -
jmap -dump:format=b,file=heap.bin <pid>
?導出堆轉儲 -
MAT分析支配樹查找泄漏點
6.2 GC調優實戰案例
場景:電商大促期間每2小時Full GC
分析:監控顯示老年代每次GC后剩余空間不足10%
解決方案:
# 原配置
-Xms8g -Xmx8g -XX:NewRatio=2# 優化后
-Xms12g -Xmx12g
-XX:NewRatio=1 # 增大新生代
-XX:SurvivorRatio=6 # 增大Eden
-XX:+UseG1GC
-XX:MaxGCPauseMillis=150