Java 堆儲存所有物件實例。物件由 new/newArray 等指令建立,無需手動釋放。GC 自動識別不再被引用的物件,回收記憶體並整理碎片,實現動態儲存管理。
| 優點 | 缺點/挑戰 |
|---|---|
| 提高開發效率,消除記憶體洩漏、雙重釋放、懸空指標 | CPU/暫停時間開銷,影響應用回應性 |
| 保護程式完整性,為安全策略基石 | 演算法不完美可能遺漏垃圾 (早期版本) |
| 碎片整理提升分配效率 | 調優複雜度高,需理解應用行為與收集器特性 |
GC Roots 包含:
從 GC Roots 出發向下搜尋,可達路徑上的物件為「存活」,不可達為「垃圾」。
| 演算法 | 原理 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 引用計數 (Reference Counting) | 每物件維護計數器,引用+1,失效-1,歸零即回收 | 即時性、無長暫停、實時系統友善 | 無法處理循環引用、計數器維護開銷大 | 早期 Python、COM、Objective-C (ARC 編譯期處理) |
| 標記-清除 (Mark-Sweep) | 掃描標記存活,清除未標記 | 解決循環引用、實作簡單 | 產生記憶體碎片、需掃描全堆 | 早期 JVM、CMS 初始標記階段 |
| 標記-整理 (Mark-Compact) | 標記後將存活物件向一端移動,更新引用 | 無碎片、分配指針碰撞快 | 移動物件需更新引用、暫停較長 | 老年代收集器 (Serial Old、Parallel Old) |
| 複製 (Copying) | 劃分 From/To 區,掃描存活物件複製到 To 區,角色互換 | 無碎片、分配極快 (指針碰撞)、適合存活率低區域 | 記憶體利用率 50%、存活率高時複製開銷大 | 新生代 (Eden + Survivor)、Serial/Parallel Scavenge |
| 收集器 | 代 | 演算法 | 執行緒 | 特色 |
|---|---|---|---|---|
| Serial | 新/老 | 複製/標記-整理 | 單執行緒 | Client 模式預設、小記憶體、無併行 |
| ParNew | 新 | 複製 | 多執行緒 | Serial 多執行緒版、配合 CMS |
| Parallel Scavenge | 新 | 複製 | 多執行緒 | 吞吐量優先、自適應調節 (GC Ergonomics) |
| Serial Old | 老 | 標記-整理 | 單執行緒 | Serial 老年代、CMS 後備 |
| Parallel Old | 老 | 標記-整理 | 多執行緒 | Parallel Scavenge 老年代、吞吐量優先 |
| CMS | 老 | 標記-清除 | 併發 | 低暫停、併發標記/清除、碎片整理需 Full GC |
| G1 | 全堆 | 標記-整理+複製 | 多執行緒併發 | 分區、可預測暫停、JDK 9+ 預設 |
| ZGC | 全堆 | 併發整理 | 多執行緒併發 | 亞毫秒暫停、彩色指針、負載閾值自適應、JDK 11+ |
| Shenandoah | 全堆 | 併發整理 | 多執行緒併發 | 併發搬移、Brooks 指針、JDK 12+ |
-Xms -Xmx # 堆初始/最大值 (建議相等避免震盪)
-Xmn # 新生代大小
-XX:SurvivorRatio # Eden:Survivor 比例
-XX:+UseG1GC # 啟用 G1
-XX:MaxGCPauseMillis=200 # G1 目標暫停
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xlog:gc*:file=gc.log # GC 日誌
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ # OOM 轉儲
GC 不是黑盒,而是基於成熟演算法與工程權衡的系統組件。理解分代假說、演算法優劣、收集器適用場景,結合業務特性 (延遲敏感 vs 吞吐優先) 選擇並調優收集器,是 Java 高階工程師的核心硬技能。