C/C++ 開發者擁有內存管理的至高權力,卻也背負著內存洩漏與溢出的沉重擔子。Java 程序員則受惠於 JVM,將內存管理交給垃圾回收器(GC)。本文系統梳理 對象存活判定、回收算法、收集器實現、內存分配策略、核心參數 五大維度,構建 GC 完整知識體系。
為每個對象添加計數器,引用+1,失效-1,歸零即回收。主流 JVM 未採用,因無法解決 循環引用 問題:
MyObject obj1 = new MyObject(); MyObject obj2 = new MyObject(); obj1.ref = obj2; obj2.ref = obj1; obj1 = null; obj2 = null; // 計數器永不歸零,實際已不可用
以 GC Roots(虛擬機棧引用、方法區靜態屬性/常量引用、本地方法棧 JNI 引用等)為起點,向下搜索形成引用鏈。不可達對象 ≠ 立即回收,需經歷 兩次標記 確認無「復活」可能(finalize() 重新建立引用)才真正判死刑。
| 算法 | 核心思想 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 標記-清除 | 標記垃圾 → 統一清除 | 基礎簡單 | 效率低;產生大量不連續碎片 | 老年代(配合整理) |
| 複製 | 內存劃兩塊,每次用一塊,存活對象複製到另一塊,再整塊清理 | 無碎片,分配極快(指針碰撞) | 浪費 50% 空間 | 新生代(存活率低) |
| 標記-整理 | 標記後,存活對象向一端移動,清理邊界外內存 | 無碎片,空間利用率高 | 移動對象開銷大 | 老年代(存活率高) |
| 分代收集 | 按對象壽命劃分新生代/老年代,分別採用最適算法 | 綜合效能最優 | 實現複雜 | 所有商業 JVM 默認 |
收集器 = 算法的具體實現,不同廠商、版本差異極大。下表梳理核心收集器特性與組合:
| 收集器 | 代別 | 算法 | 線程 | 特點/優勢 | 典型組合 |
|---|---|---|---|---|---|
| Serial | 新生代 | 複製 | 單 | Client 模式默認,簡單高效 | + Serial Old |
| ParNew | 新生代 | 複製 | 多 | Serial 多線程版,縮短停頓 | + Serial Old / CMS |
| Parallel Scavenge | 新生代 | 複製 | 多 | 關注 吞吐量,適合後台計算 | + Serial Old / Parallel Old |
| Serial Old | 老年代 | 標記-整理 | 單 | JDK 1.5 前 Parallel Scavenge 搭檔 | + Parallel Scavenge |
| Parallel Old | 老年代 | 標記-整理 | 多 | 多核優勢,配合 Parallel Scavenge 吞吐量最大化 | + Parallel Scavenge |
| CMS | 老年代 | 標記-清除 | 多 | 關注 最短停頓,並發收集,適合高交互 | ParNew + CMS + Serial Old(備用) |
CMS 關鍵細節:初始標記/重新標記需 STW,並發標記/清除不停頓;預設老年代 68% 觸發 Full GC;碎片整理參數 -XX:+UseCMSCompactAtFullCollection;G1 於 JDK 1.7 正式發布,打破代別概念,此處不再展開。
-XX:MaxTenuringThreshold 控制。-XX:PretenureSizeThreshold)。Card Table 記錄老年代→新生代引用,加速 Minor GC 掃描。-Xnoclassgc 可關閉類卸載。| 類別 | 參數 | 含義 |
|---|---|---|
| 堆設置 | -Xms | 初始堆大小 |
-Xmx | 最大堆大小 | |
-XX:NewSize | 新生代大小 | |
-XX:NewRatio | 新生代:老年代 比例(如 3 即 1:3) | |
-XX:SurvivorRatio | Eden:Survivor 比例(如 3 即 3:2,單 Survivor 佔 1/5) | |
-XX:MaxPermSize | 永久代大小(JDK 8 後為元空間) | |
| 收集器選擇 | -XX:+UseSerialGC | 串行收集器 |
-XX:+UseParallelGC | 並行收集器(吞吐量優先) | |
-XX:+UseParallelOldGC | 並行老年代收集器 | |
-XX:+UseConcMarkSweepGC | 並發收集器(停頓優先) | |
| 日誌輸出 | -XX:+PrintGCDetails | GC 詳細信息 |
-XX:+PrintHeapAtGC | GC 前後堆詳情 | |
-XX:+PrintGCTimeStamps | GC 時間戳 | |
-XX:+PrintTenuringDistribution | 對象年齡分佈 | |
-XX:+HandlePromotionFailure | 老年代分配擔保 | |
| 並行調優 | -XX:ParallelGCThreads | 並行收集線程數 |
-XX:MaxGCPauseMillis | 最大停頓時間(Parallel Scavenge) | |
-XX:GCTimeRatio | 吞吐量比例 1/(1+n) | |
| CMS 調優 | -XX:+CMSIncrementalMode | 增量模式(單 CPU) |
-XX:CMSInitiatingOccupancyFraction | 觸發 Full GC 老年代佔比(默認 68%) |
GC 是 JVM 性能調優的核心戰場。理解「判定→算法→收集器→分配策略→參數控制」完整鏈路,才能從容應對線上 OOM、長停頓、吞吐率低等疑難雜症。下一講我們將實戰 GC 日誌分析與調優案例,敬請期待。