前言
沒有完美的垃圾收集器,只有最適合具體應(yīng)用的垃圾收集器蹬蚁。
1. Serial 收集器
新生代收集器打月,最基礎(chǔ)且歷史最悠久的收集器,在 JDK 1.3.1 之前是 HotSpot 的唯一選擇湿硝。
工作圖
參數(shù)
-XX:SurvivorRatio
:
Eden 區(qū)與 Survivor 區(qū)的比例薪前。
-XX:PretenureSizeThreshold
:
晉升老年代對象大小。
優(yōu)點(diǎn)
- 足夠簡單关斜,額外內(nèi)存消耗最少
- 在單核處理器或處理器核心較少時(shí)示括,單線程收集效率最高
缺點(diǎn)
- 單線程 收集器
- 進(jìn)行垃圾收集時(shí),必須暫停其他所有工作線程
使用場景
- 單核處理器或處理器核心較少
- 運(yùn)行在客戶端模式
- 內(nèi)存資源受限
2. ParNew 收集器
新生代收集器痢畜,使用標(biāo)記 — 復(fù)制算法垛膝,實(shí)質(zhì)上為 Serial 收集器的多線程并行版本鳍侣。其控制參數(shù)、收集算法吼拥、暫停線程倚聚、對象分配規(guī)則和回收策略等都與 Serial 收集器一致,它們的實(shí)現(xiàn)代碼也有相當(dāng)多共用部分凿可。
JDK 7 以前惑折,它是不少運(yùn)行在服務(wù)端模式下的 HotSpot 虛擬機(jī)首選的新生代收集器。
JDK 9 以前枯跑,只有 Serial 和 ParNew 能與 CMS 收集器配合工作惨驶,JDK 9 開始 ParNew 只能和 CMS 收集器綁定使用。
工作圖
參數(shù)
同 Serial 收集器
優(yōu)點(diǎn)
- 支持多線程并行收集
缺點(diǎn)
- 進(jìn)行垃圾收集時(shí)敛助,必須暫停其他所有工作線程
3. Parallel Scavenge 收集器
新生代收集器粗卜,使用標(biāo)記 — 復(fù)制算法,和 ParNew 收集器一樣支持多線程并行收集辜腺,但 Parallel Scavenge 收集器更關(guān)注吞吐量休建,即 處理器用于運(yùn)行用戶代碼的時(shí)間 / 處理器總消耗時(shí)間,此處總消耗時(shí)間為 運(yùn)行用戶代碼的時(shí)間 + 運(yùn)行垃圾收集時(shí)間评疗。
停頓時(shí)間越短测砂,越適合需要與用戶交互或需要保證服務(wù)響應(yīng)質(zhì)量的程序。
吞吐量越高百匆,處理器資源利用率越高砌些,越快完成程序的運(yùn)算任務(wù),適合不需要太多交互的后臺運(yùn)算加匈、分析任務(wù)存璃。
參數(shù)
-XX:MaxGCPauseMilliis
:
最大垃圾收集停頓時(shí)間,大于 0 的毫秒數(shù)雕拼。值不宜過小纵东,會導(dǎo)致新生代空間縮小,以換取更短停頓時(shí)間啥寇,進(jìn)一步導(dǎo)致更頻繁的 GC偎球,吞吐量下降。
-XX:GCTimeRatio
:
吞吐量大小辑甜。值為吞吐量的倒數(shù)衰絮,即垃圾收集時(shí)間占總時(shí)間的比率。
-XX:+UseAdaptiveSizePolicy
:
動態(tài)設(shè)置新生代大小磷醋、Eden 與 Survivor 區(qū)的比例猫牡、晉升老年代對象大小等參數(shù),提供最合適停頓時(shí)間或最大吞吐量邓线。
優(yōu)點(diǎn)
- 支持多線程并行收集
- 自適應(yīng)調(diào)節(jié)參數(shù)
- 吞吐量相較于 ParNew 收集器更好
缺點(diǎn)
- 進(jìn)行垃圾收集時(shí)淌友,必須暫停其他所有工作線程
工作圖
4. Serial Old 收集器
老生代收集器煌恢,使用標(biāo)記 — 整理算法,Serial Old 是 Serial 收集器的老年代版本亩进,故也為單線程收集器症虑。
工作圖
優(yōu)點(diǎn)
- 足夠簡單,額外內(nèi)存消耗最少
- 在單核處理器或處理器核心較少時(shí)归薛,單線程收集效率最高
缺點(diǎn)
- 單線程 收集器
- 進(jìn)行垃圾收集時(shí),必須暫停其他所有工作線程
使用場景
- 單核處理器或處理器核心較少
- 運(yùn)行在客戶端模式
- JDK 5 及以前版本下匪蝙,與 Paraellel Scavenge 收集器搭配使用
- 作為 CMS 收集器失敗時(shí)的后備預(yù)案
5. Parallel Old 收集器
老生代收集器主籍,使用標(biāo)記 — 整理算法,Parallel Old 是 Parallel Scavenge 收集器的老年代版本逛球,故也為多線程收集器千元。
出現(xiàn)于 JDK 6,結(jié)束了此前 Parallel Scavenge 收集器只能搭配 Serial Old 收集器使用的狀態(tài)颤绕。
工作圖
優(yōu)點(diǎn)
- 支持多線程并行收集
- 自適應(yīng)調(diào)節(jié)參數(shù)
- 吞吐量相較于 ParNew 收集器更好
缺點(diǎn)
- 進(jìn)行垃圾收集時(shí)幸海,必須暫停其他所有工作線程
使用場景
- 注重吞吐量
- 處理器資源不充足
6. CMS 收集器
老生代收集器,使用標(biāo)記 — 清除算法奥务,支持多線程并行收集物独,以最短回收停頓時(shí)間為目標(biāo)的收集器。CMS 收集器在 JDK 5 中發(fā)布氯葬。
工作圖
工作流程
1. 初始標(biāo)記
僅標(biāo)記 GC Roots 能直接關(guān)聯(lián)到的對象挡篓,速度較快,但需要暫停用戶線程帚称。
2. 并發(fā)標(biāo)記
從 GC Roots 直接關(guān)聯(lián)對象開始遍歷整個(gè)對象圖的過程官研,耗時(shí)較長但不影響用戶線程運(yùn)行。采用 增量更新 的
3. 重新標(biāo)記
修正并發(fā)標(biāo)記期間闯睹,用戶線程運(yùn)行導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄戏羽,速度略慢于初始標(biāo)記,但遠(yuǎn)快于并發(fā)標(biāo)記楼吃。
4. 并發(fā)清除
清理刪掉標(biāo)記階段判斷已經(jīng)死亡的對象始花,不需要移動存活對象,故可與用戶線程并發(fā)所刀。
參數(shù)
-XX:CMSInitiatingOccupancyFraction
:
CMS 觸發(fā)垃圾回收的內(nèi)存占用比閾值
優(yōu)點(diǎn)
- 支持多線程并行收集
- 停頓時(shí)間較短
缺點(diǎn)
-
對處理器資源非常敏感
CMS 默認(rèn)啟動的回收線程數(shù)為(處理器核心數(shù)量 + 3) / 4
衙荐。當(dāng)處理器核心數(shù)在四個(gè)及以上時(shí),只占用不超過 25% 的運(yùn)行資源浮创,且核心數(shù)越多忧吟,占用越低;但核心數(shù)不足四個(gè)時(shí)斩披,回收線程對用戶程序影響會變大溜族。 -
會產(chǎn)生浮動垃圾讹俊,有可能出現(xiàn) 并發(fā)失敗,進(jìn)而執(zhí)行 Full GC煌抒,導(dǎo)致所有線程暫停
并發(fā)清理階段仍劈,用戶線程并發(fā)執(zhí)行,會伴隨新的垃圾對象產(chǎn)生寡壮,但這部分垃圾出現(xiàn)在標(biāo)記之后贩疙,無法在此次垃圾收集中清理,故留到下一次垃圾收集中清理况既,此部分垃圾被稱為浮動垃圾这溅。
在并發(fā)垃圾收集時(shí),用戶線程需要持續(xù)進(jìn)行棒仍,故需要預(yù)留足內(nèi)存空間給用戶線程使用悲靴,JDK 5 為 68%,偏保守莫其;JDK 6 為 92%癞尚,又略激進(jìn),直接導(dǎo)致面臨預(yù)留空間不足而導(dǎo)致并發(fā)失敗乱陡,并需要啟動 Serial Old 收集器進(jìn)行老年代垃圾收集浇揩,暫停用戶線程,造成更久的停頓時(shí)間蛋褥。 -
空間碎片多
空間碎片多是標(biāo)記 — 清除算法的通病临燃,這將導(dǎo)致大對象容易找不到足夠的空間進(jìn)行分配,從而觸發(fā)一次 Full GC烙心,造成停頓膜廊。
使用場景
- 追求停頓時(shí)間短的 B/S 系統(tǒng)
7. Garbage First(G1)收集器
G1 收集器可面向堆內(nèi)存任何部分組成回收集進(jìn)行回收,不再局限于新生代還是老年代淫茵。是垃圾收集器技術(shù)史上里程碑式的成果爪瓜,JDK 7 Update 4 中正式商用。
G1 收集器基于 Region 堆內(nèi)存布局匙瘪,仍然是遵循分代收集理論設(shè)計(jì)铆铆,但 G1 不再以固定大小及數(shù)量劃分分代區(qū)域,而是分成多個(gè)獨(dú)立區(qū)域(Region)丹喻。每個(gè) Region 根據(jù)需要扮演新生代的 Eden 空間薄货、Survivor 空間或老年代空間,且 不需要連續(xù)碍论,G1 收集器會對不同的角色進(jìn)行不同策略的處理谅猾。
Region 中有一類特殊的 Humongous 區(qū)域,專門用來存儲大對象。G1 收集器對大對象的定義為:超過一個(gè) Region 容量一半的對象税娜。當(dāng)對象超過整個(gè) Region 容量的超級大對象坐搔,將會被存放在多個(gè)連續(xù)的 Humongous 區(qū)域中,Humongous 區(qū)域被 G1 收集器當(dāng)作老年代看待敬矩。
Region 是最小回收單元概行,每次回收的內(nèi)存空間都是 Region 大小的整數(shù)倍。因此弧岳,G1 能建立可預(yù)測的停頓時(shí)間模型凳忙,有計(jì)劃避免全區(qū)域垃圾收集(相當(dāng)于 Full GC)。G1 收集器會根據(jù)回收所獲得空間大小及所需時(shí)間的經(jīng)驗(yàn)值禽炬,后臺維護(hù)一個(gè)優(yōu)先級列表消略,根據(jù)用戶設(shè)定允許的收集停頓時(shí)間優(yōu)先處理收益最大的 Region,故 G1 不追求一次性清理完整個(gè)堆瞎抛,只需要保證收集速度跟得上對象分配速度。
設(shè)計(jì)思路
- 局部收集
- Region 內(nèi)存布局
內(nèi)存區(qū)域圖
其中 E 為 Eden 區(qū)域却紧,S 為 Survivor 區(qū)域桐臊,H 為 Humongous 區(qū)域。
工作圖
工作原理
1. 跨 Region 引用對象的解決
G1 收集器中晓殊,每個(gè) Region 都會維護(hù)自己的記憶集断凶,像其他收集器的記憶集一樣,記憶集中記錄下對其他 Region 的指針巫俺,還會記錄別的 Region 對自己的指針认烁,并標(biāo)記這些指針在哪些卡頁范圍內(nèi),即為雙向卡表介汹。
G1 收集器的記憶集存儲結(jié)構(gòu)本質(zhì)上是一種哈希表却嗡,Key 是別的 Region 的起始地址,Value 為一個(gè)集合嘹承,存儲卡表的索引號窗价。
因記憶集比其他傳統(tǒng)收集器多很多,導(dǎo)致 G1 收集器有著更高的內(nèi)存負(fù)擔(dān)叹卷,根據(jù)經(jīng)驗(yàn)撼港,G1 至少需要 Java 堆容量 10% 到 20% 額外內(nèi)存維持收集器工作。
2. 收集線程和用戶線程的協(xié)調(diào)
- 對象引用關(guān)系改變
用戶線程改變對象引用關(guān)系時(shí)骤竹,必須保證不能打破原本的對象圖結(jié)構(gòu)帝牡,導(dǎo)致標(biāo)記錯(cuò)誤,G1 采用 原始快照 算法實(shí)現(xiàn)蒙揣。 - 新對象創(chuàng)建
G1 為每個(gè) Region 設(shè)計(jì)了兩個(gè)名為 TAMS 的指針靶溜,將 Region 中的一部分空間劃分出來用于并發(fā)回收過程中新對象分配,并發(fā)回收時(shí)新分配對象地址都必須在這兩個(gè)指針位置上鸣奔。G1 默認(rèn)在這個(gè)地址以上的對象是被隱式標(biāo)記過墨技,即默認(rèn)存活惩阶,不納入回收范圍。 - Full GC
當(dāng)內(nèi)存回收速度趕不上內(nèi)存分配速度時(shí)扣汪,G1 收集器和 CMS 收集器類似断楷,會凍結(jié)用戶線程執(zhí)行,進(jìn)行 Full GC 而產(chǎn)生停頓崭别。
3. 建立可靠的停頓預(yù)測模型
用戶通過 -XX:MaxGCPauseMillis
參數(shù)指定的停頓時(shí)間只意味著垃圾收集發(fā)生前的期望值冬筒。
G1 收集器停頓預(yù)測模型是以衰減均值為理論基礎(chǔ)實(shí)現(xiàn)的,在垃圾收集過程中茅主,G1 收集器會記錄每個(gè) Region 的回收耗時(shí)舞痰、每個(gè) Region 記憶集中臟卡數(shù)量等各個(gè)可測量步驟花費(fèi)的成本,并分析出平均值诀姚、標(biāo)準(zhǔn)偏差响牛、置信度等統(tǒng)計(jì)信息。統(tǒng)計(jì)狀態(tài)越新越能決定回收的價(jià)值赫段,通過這些信息即可預(yù)測當(dāng)前時(shí)間開始回收呀打,通過哪些 Region 組成回收集可在不超過期望停頓時(shí)間的約束下獲得最高收益。
工作流程
1. 初始標(biāo)記
僅標(biāo)記 GC Roots 能直接關(guān)聯(lián)到的對象并修改 TAMS 指針的值糯笙,讓下一個(gè)階段用戶線程并發(fā)運(yùn)行時(shí)贬丛,能正確地在可用 Region 中分配新對象。速度較快给涕,且是在 Minor GC 發(fā)生時(shí)同步完成豺憔,故實(shí)際上沒有額外的停頓。
2. 并發(fā)標(biāo)記
從 GC Roots 開始遍歷整個(gè)對象圖的過程够庙,耗時(shí)較長但不影響用戶線程運(yùn)行恭应。對象圖掃描完后,還要重新處理 原始快照 記錄下的在并發(fā)時(shí)有引用變動的對象首启。
3. 最終標(biāo)記
對用戶線程做另一個(gè)短暫的暫停暮屡,用于處理并發(fā)階段結(jié)束后仍遺留下來的最后那少量原始快照記錄。
4. 篩選回收
負(fù)責(zé)更新 Region 的統(tǒng)計(jì)數(shù)據(jù)毅桃,對各個(gè) Region 的回收價(jià)值和成本進(jìn)行排序褒纲,根據(jù)用戶所期望的停頓時(shí)間來制定回收計(jì)劃,可自由選擇任意多個(gè) Region 構(gòu)成回收集钥飞,然后把決定回收的那一部分 Region 的存活對象復(fù)制到空的 Region 中莺掠,再清理掉整個(gè)舊 Region 的全部空間。這里的操作涉及存活對象的移動读宙,是必須暫停用戶線程的彻秆,由多條收集器線程并行完成。
優(yōu)點(diǎn)
- 支持多線程并行收集
- 停頓時(shí)間低,可動態(tài)設(shè)置預(yù)期值唇兑,且在延遲可控情況下需曾,保證盡可能高的吞吐量
- Region 內(nèi)存布局使垃圾收集更加動態(tài)化狰闪,按回收收益動態(tài)回收
-
不會產(chǎn)生內(nèi)存空間碎片
G1 整體看是基于標(biāo)記 — 整理算法乍惊,但從 Region 局部看撩炊,是基于標(biāo)記 — 復(fù)制算法,兩種算法都無空間碎片留夜。
缺點(diǎn)
- 內(nèi)存匙铡、CPU 負(fù)載都比 CMS 等其他收集器高
使用場景
- 大內(nèi)存應(yīng)用
根據(jù)經(jīng)驗(yàn),通常為堆容量 6GB 到 8GB 之間的應(yīng)用碍粥,小內(nèi)存應(yīng)用中 CMS 收集器表現(xiàn)大概率好過 G1鳖眼,但 G1 仍在不斷更新優(yōu)化中,未來可期嚼摩。