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