Java垃圾收集器全解:從Serial到G1的進化之旅
----------------- 先贊后看 ?? 效果翻倍 ?? -----------------
概述
在Java的世界里,垃圾收集器就像是默默無聞的清潔工,在我們不注意的時候悄悄清理內存垃圾。不同的清潔工有不同的工作方式,有的喜歡一次性徹底打掃(Stop The World),有的則喜歡邊工作邊讓你繼續玩耍(并發收集)。今天,就讓我們一起來認識這些各具特色的"清潔工"吧!
收集器家族一覽
| 收集器 | 工作方式 | 適用分代 | 特點 | 適用場景 |
|---|---|---|---|---|
| Serial | 單線程 | 新生代 | 簡單高效,停頓時間長 | 客戶端應用,小內存 |
| ParNew | 多線程 | 新生代 | Serial的多線程版本 | 與CMS配合 |
| Parallel Scavenge | 多線程 | 新生代 | 吞吐量優先 | 后臺運算,不注重交互 |
| Serial Old | 單線程 | 老年代 | Serial的老年代版本 | 客戶端,CMS備胎 |
| Parallel Old | 多線程 | 老年代 | Parallel Scavenge的老年代版 | 吞吐量優先應用 |
| CMS | 并發 | 老年代 | 低停頓時間 | B/S系統,重視響應 |
| G1 | 并發+并行 | 全堆 | 可控停頓時間 | 大內存服務端應用 |
新生代收集器
1. Serial收集器 - "單干的老黃牛"
工作方式:單線程,Stop The World
算法:標記-復制
適用場景:客戶端模式,內存受限環境
Serial收集器就像是一位勤勞的清潔阿姨,每次打掃時都需要你暫時離開房間(Stop The World)。她工作認真,但沒有幫手,只能一件一件地清理。
詳細介紹:
Serial收集器是Java最古老的新生代收集器,采用單線程執行垃圾回收工作。當它工作時,會暫停所有應用線程(Stop The World),直到收集完成。這種"全世界暫停"的方式雖然簡單粗暴,但在特定場景下卻非常有效。
核心特點:
- 單線程工作,收集時暫停所有應用線程
- 采用標記-復制算法,將Eden區和From Survivor區的存活對象復制到To Survivor區
- 額外內存消耗最小,只需要維護一個線程的開銷
- 在單核處理器環境下效率很高,沒有線程交互開銷
配置參數:
-XX:+UseSerialGC:啟用Serial收集器-XX:SurvivorRatio:Eden與Survivor區的比例-XX:PretenureSizeThreshold:大對象直接進入老年代的閾值
適用場景:
- 客戶端應用,如桌面程序、小程序
- 小內存環境(幾十到幾百MB的新生代)
- 單核或雙核處理器環境
- 對停頓時間不敏感的應用
優缺點:
- ? 優點:實現簡單、內存開銷小、單核環境下效率高
- ? 缺點:停頓時間長、在多核環境下無法充分利用CPU資源
2. ParNew收集器 - "多人協作的清潔隊"
工作方式:多線程,Stop The World
算法:標記-復制
適用場景:與CMS配合的服務端應用
ParNew就像是Serial的升級版,從單人作戰變成了團隊協作。但它仍然需要在工作時讓所有人暫時離開房間。
詳細介紹:
ParNew收集器是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集外,其他行為與Serial收集器完全一致。它與Serial收集器共用了大量的代碼,可以看作是Serial的并行化改造。
核心特點:
- 多線程并行收集,默認線程數與CPU核心數相同
- 采用與Serial相同的標記-復制算法
- 需要Stop The World,但停頓時間比Serial短
- 與CMS收集器配合工作
配置參數:
-XX:+UseParNewGC:啟用ParNew收集器-XX:ParallelGCThreads:設置垃圾收集線程數-XX:SurvivorRatio:Eden與Survivor區的比例
適用場景:
- 與CMS收集器配合使用
- 多核處理器的服務端應用
- 對響應時間有一定要求的應用
工作流程:
- 暫停所有應用線程(Stop The World)
- 多線程并行標記存活對象
- 多線程并行復制存活對象到Survivor區
- 清理Eden區和From Survivor區
- 恢復應用線程
優缺點:
- ? 優點:多線程并行收集,停頓時間比Serial短
- ? 缺點:仍然需要Stop The World,單核環境下性能不如Serial
3. Parallel Scavenge收集器 - "效率至上的工廠流水線"
工作方式:多線程,Stop The World
算法:標記-復制
適用場景:后臺運算,吞吐量優先應用
Parallel Scavenge不像是在打掃房間,更像是在優化工廠生產線。它不關心每次停頓多久,只關心單位時間內能完成多少工作。
詳細介紹:
Parallel Scavenge收集器是一款專注于吞吐量的新生代收集器。它的目標是達到一個可控制的吞吐量(Throughput),即處理器用于運行用戶代碼的時間與處理器總消耗時間的比值。
核心特點:
- 關注點是吞吐量而非停頓時間
- 提供精確控制吞吐量的參數
- 支持自適應調節策略
- 采用多線程并行收集
配置參數:
-XX:+UseParallelGC:啟用Parallel Scavenge收集器-XX:MaxGCPauseMillis:最大垃圾收集停頓時間-XX:GCTimeRatio:垃圾收集時間與總時間的比率-XX:+UseAdaptiveSizePolicy:啟用自適應策略
自適應策略:
當啟用自適應策略后,虛擬機會根據系統運行情況自動調整以下參數:
- 新生代大小(-Xmn)
- Eden與Survivor區的比例(-XX:SurvivorRatio)
- 晉升老年代對象大小(-XX:PretenureSizeThreshold)
適用場景:
- 后臺運算任務,如批量處理、數據分析
- 對吞吐量要求高于響應時間的應用
- 不需要與用戶交互的應用
優缺點:
- ? 優點:吞吐量高、支持自適應調節、適合后臺運算
- ? 缺點:停頓時間不可控、不適合交互式應用
老年代收集器
4. Serial Old收集器 - "老黃牛的晚年生活"
工作方式:單線程,Stop The World
算法:標記-整理
適用場景:客戶端模式,CMS失敗時的備胎
Serial Old就像是Serial收集器退休后再就業,雖然年紀大了,但依然兢兢業業。
詳細介紹:
Serial Old是Serial收集器的老年代版本,同樣采用單線程工作和Stop The World的方式。它使用標記-整理算法,避免內存碎片的產生。
核心特點:
- 單線程工作,需要Stop The World
- 采用標記-整理算法
- 內存開銷小
- 作為CMS失敗時的備胎
工作流程:
- 標記階段:標記所有存活對象
- 整理階段:將所有存活對象向一端移動
- 清理階段:清理邊界以外的內存
適用場景:
- 客戶端模式下的老年代收集
- 與Parallel Scavenge搭配使用(JDK5及之前)
- CMS收集器發生Concurrent Mode Failure時的后備預案
配置參數:
-XX:+UseSerialGC:啟用Serial Old收集器- 通常與其他收集器配合使用,不需要單獨配置
優缺點:
- ? 優點:實現簡單、內存開銷小、不會產生內存碎片
- ? 缺點:停頓時間長、單線程效率低
5. Parallel Old收集器 - "流水線的完美搭檔"
工作方式:多線程,Stop The World
算法:標記-整理
適用場景:與Parallel Scavenge搭配的吞吐量優先應用
Parallel Old的出現讓Parallel Scavenge終于有了門當戶對的搭檔,形成了真正的"吞吐量優先"組合。
詳細介紹:
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多線程并發收集,基于標記-整理算法實現。它在JDK6中才開始提供,填補了Parallel Scavenge沒有匹配老年代收集器的空白。
核心特點:
- 多線程并行收集
- 采用標記-整理算法
- 專注于吞吐量優化
- 與Parallel Scavenge完美配合
工作流程:
- 標記階段:多線程并行標記存活對象
- 整理階段:多線程并行整理內存
- 清理階段:清理不可達對象
適用場景:
- 與Parallel Scavenge搭配使用
- 注重吞吐量的應用
- 后臺運算任務
配置參數:
-XX:+UseParallelOldGC:啟用Parallel Old收集器- 通常與Parallel Scavenge一起使用
優缺點:
- ? 優點:吞吐量高、多線程并行、與Parallel Scavenge配合良好
- ? 缺點:停頓時間較長、不適合交互式應用
6. CMS收集器 - "邊開派對邊打掃的管家"
工作方式:并發,低停頓
算法:標記-清除
適用場景:B/S系統,重視響應速度的應用
CMS就像是一位技藝高超的管家,能夠在派對進行中悄悄打掃,盡量不打擾賓客的雅興。
詳細介紹:
CMS(Concurrent Mark Sweep)收集器是一款以獲取最短回收停頓時間為目標的收集器,非常適合重視響應速度的應用。它采用標記-清除算法,盡可能減少Stop The World的時間。
工作流程:
- 初始標記:標記GC Roots直接關聯的對象(需要Stop The World,但很快)
- 并發標記:并發遍歷對象圖,標記所有可達對象
- 重新標記:修正并發標記期間變動的標記記錄(需要Stop The World)
- 并發清除:并發清理垃圾對象
核心特點:
- 并發收集,低停頓時間
- 采用標記-清除算法,會產生內存碎片
- 對處理器資源敏感
- 無法處理"浮動垃圾"
配置參數:
-XX:+UseConcMarkSweepGC:啟用CMS收集器-XX:CMSInitiatingOccupancyFraction:老年代使用率觸發閾值-XX:+UseCMSCompactAtFullCollection:Full GC時開啟內存壓縮-XX:CMSFullGCsBeforeCompaction:設置多少次Full GC后壓縮一次
浮動垃圾問題:
由于CMS的并發清理階段用戶線程仍在運行,會產生新的垃圾對象,這些對象無法在本次收集中處理,稱為"浮動垃圾"。
適用場景:
- B/S系統服務端
- 重視響應速度的應用
- 互聯網應用
優缺點:
- ? 優點:并發收集、低停頓時間、適合交互式應用
- ? 缺點:對CPU資源敏感、會產生內存碎片、無法處理浮動垃圾
全堆收集器:G1 - "智能分區清潔系統"
工作方式:并發+并行
算法:整體標記-整理,局部標記-復制
適用場景:大內存服務端應用,兼顧吞吐量和停頓時間
G1收集器就像是現代智能清潔系統,將房間分成多個區域,每次只清理最臟的區域,并且能夠預測清理需要的時間。
詳細介紹:
G1(Garbage First)收集器是垃圾收集器技術發展歷史上的里程碑成果,開創了面向局部收集的設計思路和基于Region的內存布局形式。它旨在替代CMS收集器,提供更可控的停頓時間。
革命性創新:
- 基于Region的堆內存布局:將堆劃分為多個大小相等的Region
- 可預測的停頓時間模型:通過
-XX:MaxGCPauseMillis參數設定期望停頓時間 - 面向局部收集:不再堅持固定分代,而是選擇回收價值最大的Region
- Humongous區域:專門處理大對象
工作流程:
- 初始標記:標記GC Roots直接關聯的對象
- 并發標記:并發進行可達性分析
- 最終標記:處理并發標記期間的引用變動
- 篩選回收:根據回收價值排序,選擇Region進行回收
配置參數:
-XX:+UseG1GC:啟用G1收集器-XX:MaxGCPauseMillis:設置最大停頓時間目標-XX:G1HeapRegionSize:設置Region大小-XX:InitiatingHeapOccupancyPercent:觸發并發標記周期的Java堆占用率閾值
適用場景:
- 大內存服務端應用(6GB以上)
- 需要兼顧吞吐量和停頓時間的應用
- 替代CMS收集器
優缺點:
- ? 優點:停頓時間可控、高吞吐量、不會產生內存碎片
- ? 缺點:內存占用較高、寫屏障實現復雜
如何選擇收集器?
選擇垃圾收集器就像選擇清潔方式,需要根據實際情況決定:
-
小內存客戶端應用(小于512MB)
- Serial + Serial Old組合
- 簡單高效,內存開銷小
-
重視響應速度的B/S系統
- ParNew + CMS組合
- 低停頓時間,適合交互式應用
-
后臺運算,注重吞吐量
- Parallel Scavenge + Parallel Old組合
- 高吞吐量,適合批處理任務
-
大內存服務端應用(大于6GB)
- G1收集器
- 兼顧吞吐量和停頓時間
-
超大內存應用(大于32GB)
- 考慮ZGC或Shenandoah
- 極低停頓時間,適合超大內存
選擇建議:
- 先明確應用需求:吞吐量優先還是響應時間優先
- 根據堆內存大小選擇
- 在生產環境中進行實際測試驗證
- 隨著JDK版本更新,優先考慮 newer 收集器
結語
Java垃圾收集器的發展歷程就像清潔方式的進化:從最初需要全員離開的徹底打掃(Serial),到可以邊工作邊打掃的智能清潔(CMS),再到分區打掃、時間可控的現代清潔系統(G1)。每種收集器都有其適用場景,沒有絕對的最好,只有最適合的。
隨著JDK的持續發展,更新的收集器如ZGC、Shenandoah也在不斷涌現,它們提供了更低的停頓時間和更好的性能表現。但理解這些基礎收集器的工作原理,仍然是我們優化Java應用性能的基石。
希望本文能幫助你更好地理解Java垃圾收集器,選擇適合的"清潔工",讓你的Java應用跑得更加順暢!記住,垃圾收集器的調優是一個持續的過程,需要根據實際應用特點和負載變化進行調整。
?? 如果你喜歡這篇文章,請點贊支持! ?? 同時歡迎關注我的博客,獲取更多精彩內容!
本文來自博客園,作者:佛祖讓我來巡山,轉載請注明原文鏈接:http://www.rzrgm.cn/sun-10387834/p/19094842

浙公網安備 33010602011771號