JVM常用垃圾收集器及GC算法解讀
GC的種類
1.Minor GC
對(duì)象從新生代區(qū)域消失的過(guò)程,我們稱之為 “minor GC”。
清理整個(gè)YouGen(年輕代)
的過(guò)程,eden、S0\S1的清理都會(huì)由于MinorGC Allocation Failure(YoungGen區(qū)內(nèi)存不足),而觸發(fā)minorGC。
2.Major GC
OldGen區(qū)內(nèi)存不足,觸發(fā)Major GC。
3.Full GC
Full GC 是清理整個(gè)堆空間—包括年輕代和永久代。
Full GC 觸發(fā)的場(chǎng)景:
1)手動(dòng)調(diào)用 System.gc()
2)promotion failed
(年代晉升失敗,比如eden區(qū)的存活對(duì)象晉升到S區(qū)放不下,又嘗試直接晉 升到Old區(qū)又放不下,那么Promotion Failed,會(huì)觸發(fā)FullGC)
3)CMS的Concurrent-Mode-Failure
由于CMS回收過(guò)程中主要分為四步:
- 1.CMS initial mark
- 2.CMS Concurrent mark
- 3.CMS remark
- 4.CMS Concurrent sweep。在2中g(shù)c線程與用戶線程同時(shí)執(zhí)行,那么用戶線程依舊可 能同時(shí)產(chǎn)生垃圾,如果這個(gè)垃圾較多無(wú)法放入預(yù)留的空間就會(huì)產(chǎn)生CMS-Mode-Failure, 切換 為SerialOld單線程做mark-sweep-compact。
4)新生代晉升的平均大小大于老年代
的剩余空間 (為了避免新生代晉升到老年代失?。?當(dāng)使用G1,CMS 時(shí),F(xiàn)ullGC發(fā)生的時(shí)候 是 Serial+SerialOld。 當(dāng)使用ParalOld時(shí),F(xiàn)ullGC發(fā)生的時(shí)候是ParallNew +ParallOld.
GC的判定
1.引用計(jì)數(shù)法:指的是如果某個(gè)地方引用了這個(gè)對(duì)象就+1,如果失效了就-1,當(dāng)為 0 就會(huì)回收但是 JVM 沒(méi)有用這種方式,因?yàn)闊o(wú)法判定相互循環(huán)引用(A 引用 B,B 引用 A)的情況。
2.引用鏈法(可達(dá)性分析/根搜索): 通過(guò)一種 GC ROOT 的對(duì)象(方法區(qū)中靜態(tài)變量引用的對(duì)象等-static 變量)來(lái)判斷,如果有一條鏈能夠到達(dá) GC ROOT 就說(shuō)明該對(duì)象不能回首,不能到達(dá) GC ROOT 就說(shuō)明可以回收
GCRoots有哪些
- 類,由系統(tǒng)類加載器加載的類。這些類從不會(huì)被卸載,它們可以通過(guò)靜態(tài)屬性的方式持有對(duì)象的引用。
- 注意,一般情況下由自定義的類加載器加載的類不能成為GC Roots
- 線程,存活的線程
- Java方法棧中的局部變量或者參數(shù)
- JNI方法棧中的局部變量或者參數(shù)
- JNI全局引用
- 用做同步監(jiān)控的對(duì)象
- 被JVM持有的對(duì)象,這些對(duì)象由于特殊的目的不被GC回收。這些對(duì)象可能是系統(tǒng)的類加載器,一些重要的異常處理類,一些為處理異常預(yù)留的對(duì)象,以及一些正在執(zhí)行類加載的自定義的類加載器。但是具體有哪些前面提到的對(duì)象依賴于具體的JVM實(shí)現(xiàn)。
但是當(dāng)滿足上述條件時(shí),一個(gè)對(duì)象比不一定會(huì)被回收
。
當(dāng)一個(gè)對(duì)象不可達(dá) GC Root 時(shí),這個(gè)對(duì)象并不會(huì)立馬被回收
,而是出于一個(gè)死緩的階段,若要被真正的回收需要經(jīng)歷兩次標(biāo)記。
- 如果對(duì)象在可達(dá)性分析中沒(méi)有與 GC Root 的引用鏈,那么此時(shí)就會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選的條件是是否有必要執(zhí)行 finalize()方法。當(dāng)對(duì)象沒(méi)有覆蓋 finalize()方法或者已被虛擬機(jī)調(diào)用過(guò),那么就認(rèn)為是沒(méi)必要的。
- 如果該對(duì)象有必要執(zhí)行 finalize()方法,那么這個(gè)對(duì)象將會(huì)放在一個(gè)稱為 F-Queue 的對(duì)隊(duì)列中,虛擬機(jī)會(huì)觸發(fā)一個(gè) Finalize()線程去執(zhí)行,此線程是低優(yōu)先級(jí)的,并且虛擬機(jī)不會(huì)承諾一直等待它運(yùn)行完,這是
- 因?yàn)槿绻?finalize()執(zhí)行緩慢或者發(fā)生了死鎖,那么就會(huì)造成 F- Queue 隊(duì)列一直等待,造成了內(nèi)存回收系統(tǒng)的崩潰。GC 對(duì)處于 F-Queue 中的對(duì)象進(jìn)行。
第二次被標(biāo)記,這時(shí),該對(duì)象將被移除”即將回收”集合,等待回收。
GC垃圾收集器
新生代收集器
1.Serial 垃圾收集器(單線程、復(fù)制算法)
串行收集器是最古老,最穩(wěn)定以及效率高的收集器,使用停止復(fù)制方法,只使用一個(gè)線程去串行回收;垃圾收集的過(guò)程中會(huì)Stop The World(服務(wù)暫停)
;參數(shù)控制:使用-XX:+UseSerialGC可以使用Serial+Serial Old模式運(yùn)行進(jìn)行內(nèi)存回收(這也是虛擬機(jī)在Client模式下運(yùn)行的默認(rèn)值)
缺點(diǎn):是串行效率較低。
2.ParNew 垃圾收集器(Serial+多線程)
ParNew收集器其實(shí)就是Serial收集器的多線程版本,使用停止復(fù)制方法。新生代并行,其它工作線程暫停。
參數(shù)控制:使用-XX:+UseParNewGC
開(kāi)關(guān)來(lái)控制使用ParNew+Serial Old
收集器組合收集內(nèi)存;使用-XX:ParallelGCThreads
來(lái)設(shè)置執(zhí)行內(nèi)存回收的線程數(shù)。
3.Parallel Scavenge 收集器(多線程復(fù)制算法、高效)JDK1.8 默認(rèn)采用
的新生代收集器。
ParallelScavenge收集器類似ParNew收集器,Parallel收集器更關(guān)注CPU吞吐量,即運(yùn)行用戶代碼的時(shí)間/總時(shí)間,使用停止復(fù)制算法??梢酝ㄟ^(guò)參數(shù)來(lái)打開(kāi)自適應(yīng)調(diào)節(jié)策略,虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或最大的吞吐量;也可以通過(guò)參數(shù)控制GC的時(shí)間不大于多少毫秒或者比例。
參數(shù)控制:使用-XX:+UseParallelGC開(kāi)關(guān)控制使用Parallel Scavenge+Serial Old收集器組合回收垃圾(這也是在Server模式下的默認(rèn)值);使用-XX:GCTimeRatio
來(lái)設(shè)置用戶執(zhí)行時(shí)間占總時(shí)間的比例,默認(rèn)99,即1%的時(shí)間用來(lái)進(jìn)行垃圾回收。使用-XX:MaxGCPauseMillis
設(shè)置GC的最大停頓時(shí)間(這個(gè)參數(shù)只對(duì)Parallel Scavenge有效),用開(kāi)關(guān)參數(shù)-XX:+UseAdaptiveSizePolicy
可以進(jìn)行動(dòng)態(tài)控制
,如自動(dòng)調(diào)整Eden/Survivor比例,老年代對(duì)象年齡,新生代大小等,這個(gè)參數(shù)在ParNew下沒(méi)有。
老年代收集器
1.Serial Old收集器(單線程標(biāo)記整理算法 )
老年代收集器,單線程收集器,串行,使用"標(biāo)記-整理"算法(整理的方法是Sweep(清理)和Compact(壓縮),主要是運(yùn)行在 Client 默認(rèn)的 java 虛擬機(jī)默認(rèn)的年老代垃圾收集器。
2.Parallel Scavenge 收集器
多線程機(jī)制與Parallel Scavenge差不錯(cuò),使用標(biāo)記整理
(與Serial Old不同,這里的整理是Summary(匯總)和Compact(壓縮),匯總的意思就是將幸存的對(duì)象復(fù)制到預(yù)先準(zhǔn)備好的區(qū)域,而不是像Sweep(清理)那樣清理廢棄的對(duì)象)算法,在Parallel Old執(zhí)行時(shí),仍然需要暫停
其它線程。Parallel Old在多核計(jì)算中很有用。這個(gè)收集器是在JDK 1.6中,與Parallel Scavenge
配合有很好的效果。
參數(shù)控制: 使用-XX:+UseParallelOldGC
開(kāi)關(guān)控制使用Parallel Scavenge +Parallel Old組合收集 器進(jìn)行收集。
3.CMS 收集器(多線程標(biāo)記清除算法)
Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標(biāo)是獲取最短垃圾回收停頓時(shí)間
,和其他年老代使用標(biāo)記-整理算法不同,它使用多線程的標(biāo)記-清除算法。
整個(gè)過(guò)程分為6個(gè)步驟,其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop The World
”
- 初始標(biāo)記
- 只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)的對(duì)象,速度很快,仍然需要暫停所有的工作線程。
- 并發(fā)標(biāo)記
- 進(jìn)行 GC Roots 跟蹤的過(guò)程,和用戶線程一起工作,不需要暫停工作線程。
- 重新標(biāo)記
- 為了修正在并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,仍然
- 需要暫停所有的工作線程。
- 并發(fā)清除
- 清除 GC Roots 不可達(dá)對(duì)象,和用戶線程一起工作,不需要暫停工作線程。由于耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中,垃圾收集線程可以和用戶現(xiàn)在一起并發(fā)工作,所以總體上來(lái)看CMS 收集器的內(nèi)存回收和用戶線程是一起并發(fā)地執(zhí)行。
優(yōu)點(diǎn):并發(fā)收集、低停頓
缺點(diǎn):產(chǎn)生大量空間碎片、并發(fā)階段會(huì)降低吞吐量
4.G1收集器
Garbage first 垃圾收集器是目前垃圾收集器理論發(fā)展的最前沿成果,相比與 CMS 收集器,G1 收集器兩個(gè)最突出的改進(jìn)是:
- 基于標(biāo)記-整理算法,不產(chǎn)生內(nèi)存碎片。
- 可以非常精確控制停頓時(shí)間,在不犧牲吞吐量前提下,實(shí)現(xiàn)低停頓垃圾回收。
G1特點(diǎn):
- G1通過(guò)將內(nèi)存空間分成區(qū)域(Region)的方式避免內(nèi)存碎片問(wèn)題
- Eden, Survivor, Old區(qū)不再固定、在內(nèi)存使用效率上來(lái)說(shuō)更靈活
- G1可以通過(guò)設(shè)置預(yù)期停頓時(shí)間(Pause Time)來(lái)控制垃圾收集時(shí)間避免應(yīng)用雪崩現(xiàn)象
- G1在回收內(nèi)存后會(huì)馬上同時(shí)做合并空閑內(nèi)存的工作、而CMS默認(rèn)是在STW(stop the world)的時(shí)候做
- G1會(huì)在Young GC中使用、而CMS只能在O區(qū)使用
G1 收集器避免全區(qū)域垃圾收集,它把堆內(nèi)存劃分為大小固定的幾個(gè)獨(dú)立區(qū)域,并且跟蹤這些區(qū)域的垃圾收集進(jìn)度,同時(shí)在后臺(tái)維護(hù)一個(gè)優(yōu)先級(jí)列表,每次根據(jù)所允許的收集時(shí)間,優(yōu)先回收垃圾最多的區(qū)域。區(qū)域劃分和優(yōu)先級(jí)區(qū)域回收機(jī)制,確保 G1 收集器可以在有限時(shí)間獲得最高的垃圾收集效率。
GC垃圾收集器的選擇
JVM給了三種選擇:串行收集器、并行收集器、并發(fā)收集器。
但是串行收集器只適用于小數(shù)據(jù)量的情況,所以這里的選擇主要針對(duì)并行收集器和并發(fā)收集器。
吞吐量?jī)?yōu)先的并行收集器
如上文所述,并行收集器主要以到達(dá)一定的吞吐量為目標(biāo),適用于科學(xué)技術(shù)和后臺(tái)處理等。Parallel Scavenge + ParallelOld
響應(yīng)時(shí)間優(yōu)先的并發(fā)收集器
如上文所述,并發(fā)收集器主要是保證系統(tǒng)的響應(yīng)時(shí)間,減少垃圾收集時(shí)的停頓時(shí)間。適用于應(yīng)用服務(wù)器、電信領(lǐng)域ParNew + CMS
GC垃圾收集算法
標(biāo)記-清除算法 (Mark Sweep)
算法分為2個(gè)階段:
- 1.標(biāo)記處需要回收的對(duì)象
- 2.回收被標(biāo)記的對(duì)象。
標(biāo)記算法分為兩種:
1.引用計(jì)數(shù)算法(Reference Counting)
2.可達(dá)性分析算法(Reachability Analysis)。由于引用技術(shù)算法無(wú)法解決循環(huán)引用的問(wèn)題,所以這里使用的標(biāo)記算法均為可達(dá)性分析算法。
缺點(diǎn):產(chǎn)生了大量的非連續(xù)內(nèi)存,內(nèi)存碎片化嚴(yán)重,后續(xù)可能發(fā)生大對(duì)象不能找到可利用空間的問(wèn)題。
復(fù)制算法 (Copying)
為了解決效率與內(nèi)存碎片問(wèn)題,復(fù)制(Copying)算法出現(xiàn)了,它將內(nèi)存劃分為兩塊相等的大小,每次使用一塊,當(dāng)這一塊用完了,就講還存活的對(duì)象復(fù)制到另外一塊內(nèi)存區(qū)域中,然后將當(dāng)前內(nèi)存空間一次性清理掉。這樣的對(duì)整個(gè)半?yún)^(qū)進(jìn)行回收,分配時(shí)按照順序從內(nèi)存頂端依次分配,這種實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。
不過(guò)這種算法將原有的內(nèi)存空間減少為實(shí)際的一半,代價(jià)比較高。
缺點(diǎn):可用內(nèi)存變?yōu)樵瓉?lái)的一半,長(zhǎng)期存活的對(duì)象復(fù)制頻率高
標(biāo)記-整理算法(Mark-Compact)
標(biāo)記階段和 Mark-Sweep 算法相同,標(biāo)記后不是清理對(duì)象,而是將存活對(duì)象移向內(nèi)存的一端。然后清除端邊界外的對(duì)象
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java利用Geotools從DEM數(shù)據(jù)中讀取指定位置的高程信息全過(guò)程
Geotools作為一款功能強(qiáng)大且開(kāi)源的地理工具庫(kù),為地理數(shù)據(jù)的處理和分析提供了豐富的類庫(kù)和便捷的接口,能夠很好地滿足從DEM數(shù)據(jù)中讀取高程信息這一實(shí)戰(zhàn)需求,本文將深入講解如何利用Geotools從獲取DEM數(shù)據(jù)到成功讀取指定位置高程信息的全過(guò)程,需要的朋友可以參考下2025-03-03Java 實(shí)現(xiàn)加密數(shù)據(jù)庫(kù)連接的步驟
這篇文章主要介紹了Java 實(shí)現(xiàn)加密數(shù)據(jù)庫(kù)連接的步驟,幫助大家更好的理解和使用Java處理數(shù)據(jù)庫(kù),感興趣的朋友可以了解下2020-11-11?Java數(shù)據(jù)結(jié)構(gòu)的十大排序
這篇文章主要介紹了?Java數(shù)據(jù)結(jié)構(gòu)的十大排序,排序算法分為比較類排序和非比較類排序,具體的內(nèi)容,需要的朋友參考下面思維導(dǎo)圖及文章介紹,希望對(duì)你有所幫助2022-01-01手動(dòng)部署java項(xiàng)目到k8s中的實(shí)現(xiàn)
本文主要介紹了手動(dòng)部署java項(xiàng)目到k8s中的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Spring MVC Mybatis多數(shù)據(jù)源的使用實(shí)例解析
項(xiàng)目需要從其他網(wǎng)站獲取數(shù)據(jù),因?yàn)槭桥R時(shí)加的需求,這篇文章主要介紹了Spring MVC Mybatis多數(shù)據(jù)源的使用實(shí)例解析,需要的朋友可以參考下2016-12-12SpringBoot+Shiro+Redis+Mybatis-plus 實(shí)戰(zhàn)項(xiàng)目及問(wèn)題小結(jié)
最近也是一直在保持學(xué)習(xí)課外拓展技術(shù),所以想自己做一個(gè)簡(jiǎn)單小項(xiàng)目,于是就有了這個(gè)快速上手 Shiro 和 Redis 的小項(xiàng)目,說(shuō)白了就是拿來(lái)練手調(diào)調(diào) API,然后做完后拿來(lái)總結(jié)的小項(xiàng)目,感興趣的朋友一起看看吧2021-04-04SpringBoot開(kāi)發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決
這篇文章主要介紹了SpringBoot開(kāi)發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11