GC的三種收集方法詳解
1. 標記-清除算法 (Mark-Sweep)
原理
- 標記階段:從根對象(GC Roots)開始,標記所有可達的對象
- 清除階段:掃描整個內存區域,回收所有未被標記的對象
特點
- 優點:實現簡單,不需要對象移動
-
缺點:
- 產生內存碎片,導致大對象可能無法找到連續內存空間而提前觸發Full GC
- 標記和清除兩個階段都需要掃描整個堆內存,效率較低
應用場景
- 主要用於老年代(Old Generation)
- 適用於對象存活率高、對象生命週期長的區域
- CMS收集器的併發清除階段採用了標記-清除算法
2. 標記-整理算法 (Mark-Compact)
原理
- 標記階段:與標記-清除算法相同,標記所有可達對象
- 整理階段:將所有存活對象向一端移動,然後清理邊界外的內存
特點
-
優點:
- 解決了標記-清除算法的內存碎片問題
- 為大對象分配提供了連續內存空間
-
缺點:
- 需要額外的移動對象操作,增加了GC暫停時間
- 實現複雜度高於標記-清除算法
應用場景
- 主要用於老年代(Old Generation)
- 適用於對象存活率高且對內存碎片敏感的場景
- Serial Old、Parallel Old和G1收集器的老年代回收階段都採用了標記-整理算法
3. 複製算法 (Copying)
原理
- 將內存空間劃分為大小相等的兩塊(通常為From空間和To空間)
- 只使用其中一塊(From),另一塊(To)保持空閒
- GC時,將From空間中的存活對象複製到To空間
- 清除From空間,然後交換From和To的角色
特點
-
優點:
- 不會產生內存碎片
- 分配內存時只需移動指針,實現簡單高效
- 複製成本與存活對象數量成正比,存活對象少時效率高
-
缺點:
- 內存利用率低,只使用了一半的內存空間
- 當對象存活率高時,複製操作成本高
應用場景
- 主要用於新生代(Young Generation)
- 特別適合對象存活率低、頻繁創建和銷燬的區域
- 大多數垃圾收集器在新生代都採用複製算法(如Serial、ParNew、Parallel Scavenge)
-
實際應用中通常使用改進版的複製算法:
- Eden:Survivor = 8:1:1(一個Eden區和兩個Survivor區)
- 只需要10%的內存空間作為Survivor區即可滿足需求
三種算法的對比總結
| 算法 | 主要優點 | 主要缺點 | 適用區域 | 典型應用 |
|---|---|---|---|---|
| 標記-清除 | 不需要移動對象 | 產生內存碎片、效率較低 | 老年代 | CMS收集器 |
| 標記-整理 | 無內存碎片 | 需要移動對象、暫停時間長 | 老年代 | Serial Old、Parallel Old |
| 複製算法 | 無碎片、分配高效 | 內存利用率低 | 新生代 | 大多數新生代收集器 |
分代收集策略中的應用
在現代JVM中,通常採用分代收集策略,將堆分為新生代和老年代,根據不同區域的特點選擇合適的收集算法:
- 新生代:對象生命週期短,存活率低,適合使用複製算法
- 老年代:對象生命週期長,存活率高,適合使用標記-清除或標記-整理算法
這種分代策略充分利用了不同算法的優勢,在垃圾回收效率和內存利用率之間取得了平衡。