JVM的7種垃圾回收器(小結(jié))
垃圾回收算法和垃圾回收器
對(duì)于JVM的垃圾回收算法有復(fù)制算法、標(biāo)記清除、標(biāo)記整理。
用陽哥的話就是:這些算法只是天上飛的理念,是一種方法論,但是真正的垃圾回收還需要有落地實(shí)現(xiàn),所以垃圾回收器應(yīng)運(yùn)而生。
JVM回收的區(qū)域包括方法區(qū)和堆,jvm對(duì)于不同區(qū)域不同的特點(diǎn)采用分代收集算法,比如因?yàn)樗械膶?duì)象都是在Eden區(qū)進(jìn)行分配,并且大部分對(duì)象的存活時(shí)間都不長,都是“朝生夕死”的,每次新生代存活的對(duì)象都不多,所以新采取復(fù)制算法;而jvm默認(rèn)是新生代的對(duì)象熬過15次GC才能進(jìn)入老年代,所以老年代的對(duì)象都是生命周期比較長的,采用標(biāo)記清除或者標(biāo)記整理算法。
那么對(duì)于這些算法的實(shí)現(xiàn)都有什么呢?
新生代:serial、ParNew、Parallel
老年代:Serial Old、Parallel Old、CMS
全堆:G1
并且他們的搭配組合如下:
垃圾回收器
jvm的垃圾回收器大體上的分類主要包括四種:串行、并行、并發(fā)(CMS)和G1。
串行垃圾回收器(Serial):它為單線程環(huán)境設(shè)計(jì)并且只使用一個(gè)線程進(jìn)行垃圾回收,會(huì)暫停所有的用戶線程。所以不適合服務(wù)器環(huán)境。
并行垃圾回收器(Parallel):多個(gè)垃圾回收線程并行工作,此時(shí)用戶線程是暫停的,適用于科學(xué)計(jì)算/大數(shù)據(jù)處理等弱交互場(chǎng)景。
并發(fā)垃圾回收器(CMS):用戶線程和垃圾收集線程同時(shí)執(zhí)行(不一定是并行,可能交替執(zhí)行),不需要停頓用戶線程?;ヂ?lián)網(wǎng)公司多用它,適用于對(duì)響應(yīng)時(shí)間有要求的場(chǎng)景。
G1垃圾回收器:G1垃圾回收器將堆內(nèi)存分割成不同的區(qū)域然后并發(fā)的對(duì)其進(jìn)行垃圾回收。
默認(rèn)的垃圾回收器
平時(shí)我們沒有配置什么jvm參數(shù),程序也能正常執(zhí)行,那么JVM默認(rèn)的垃圾回收器是什么呢?
那么如何查看默認(rèn)的回收器呢?有很多方式,這里簡單列舉幾種:
1.命令行方式:
java -XX:+PrintCommandLineFlags -version
可以看到j(luò)dk8默認(rèn)的是使用的Parallel并行回收器。
2、jvm參數(shù)設(shè)置
在JVM運(yùn)行之前加入?yún)?shù)同樣可以查看,其實(shí)這兩種方式是差不多的
3.jps+jinfo
先使用jps查看java進(jìn)程號(hào),在使用jinfo查看該進(jìn)程的配置
Serial收集器
Serial是一個(gè)單線程收集器,在進(jìn)行垃圾收集的時(shí)候必須停下所有的工作(Stop The World) 。
串行收集器是最古老,最穩(wěn)定以及效率高的收集器,只使用一個(gè)線程去回收但其在進(jìn)行垃圾收集過程中可能會(huì)產(chǎn)生較長的停頓(
Stop-The-World狀態(tài))。
雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對(duì)于限定單個(gè)CPU環(huán)境來說,沒有線程交互的開銷可以獲得最高的單線程垃圾收集效率,因此Serial垃圾收集器依然是java虛擬機(jī)運(yùn)行在Client模式下默認(rèn)的新生代垃圾收集器。
對(duì)應(yīng)JVM參數(shù)是: -XX:+UseSerialGC
開啟后會(huì)使用: **Serial(Young區(qū)用) + Serial Old(Old區(qū)用)**的收集器組合:表示新生代、老年代都會(huì)使用串行回收收集器,新生代使用復(fù)制算法,老年代使用標(biāo)記-整理算法。
ParNew收集器
ParNew是Serial收集器的升級(jí)版,將單線程進(jìn)行垃圾回收升級(jí)為多線程進(jìn)行垃圾回收,但是依舊會(huì)Stop The World。
ParNew收集器其實(shí)就是Serial收集器新生代的并行多線程版本,最常見的應(yīng)用場(chǎng)景是配合老年代的CMS GC工作,其余的行為和
Serial收集器完全一樣,ParNew垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程。它是很多java虛擬機(jī)運(yùn)行在Server
模式下新生代的默認(rèn)垃圾收集器。
常用對(duì)應(yīng)JVM參數(shù): -XX:+UseParNewGC
啟用ParNew收集器,只影響新生代的收集,不影響老年代
開啟。上述參數(shù)后,會(huì)使用: ParNew(Young區(qū)用) + Serial Old的收集器組合,新生代使用復(fù)制算法,老年代采用標(biāo)記-整理算法。
但是,ParNew+Tenured這樣的搭配,java8已經(jīng)不再被推薦。
Parallel收集器
Parallel Scavenge收集器類似ParNew也是一個(gè)新生代垃圾收集器,使用復(fù)制算法,也是一個(gè)并行的多線程的垃圾收集器,俗稱吞吐
量優(yōu)先收集器。一句話:串行收集器在新生代和老年代的并行化
首先先科普下什么是吞吐量:
吞吐量(Thoughput)=運(yùn)行用戶代碼時(shí)間(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間),也即比如程序運(yùn)行100分鐘,垃圾收集時(shí)間1分鐘,
吞吐量就是99%)。
Parallel收集器重點(diǎn)關(guān)注的是:
可控制的高吞吐量意味著高效利用CPU的時(shí)間,它多用于在后臺(tái)運(yùn)算而不需要太多交互的任務(wù)。
**自適應(yīng)調(diào)節(jié)策略也是Parallel Scavenge收集器與ParNew收集器的-一個(gè)重要區(qū)別。**自適應(yīng)調(diào)節(jié)策略:虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間(-XX:MaxGCPauseMillis)或最大的吞吐量。
常用JVM參數(shù): -XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)使用Parallel Scanvenge收集器
開啟該參數(shù)后:使用Parallel收集器+Parallel Old的組合。新生代使用復(fù)制算法,老年代使用標(biāo)記-整理算法。
Serial Old收集器
SerialOlid是Serial垃圾收集器老年代版本,它同樣是個(gè)單線程的收集器,使用標(biāo)記-整理算法,這個(gè)收集器也主要是運(yùn)行在Client默
的java虛擬機(jī)默認(rèn)的年老代垃圾收集器。
在Server模式下,主要有兩個(gè)用途(了解,版本已經(jīng)到8及以后): .
1.在JDK1.5之前版本中與新生代的Parallel Scavenge收集器搭配使用。 ( Parallel Scavenge + Serial Old )
2.作為老年代版中使用CMS收集器的后備垃圾收集方案。
Parallel Old收集器
Parallel Old收集器是Parallel Scavenge的老年代版木,使用多線程的標(biāo)記-整理算法,Parallel Old收集器在JDK1.6才開始提供。
在JDK1.6之前,新生代使用Parallel Scavenge收集器只能搭配年老代的Serial Old收集器,只能保證新生代的吞吐量優(yōu)先,無法保
證整體的吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old )
Parallel Old正是為了在年老代同樣提供吞吐量優(yōu)先的垃圾收集器,如果系統(tǒng)對(duì)吞吐量要求比較高,JDK1.8后可以優(yōu)先考慮新生代
Parallel Scavenge和年老代Parallel Old收集器的搭配策略。在 JDK1.8及后(Parallel Scavenge + Parallel Old )
JVM常用參數(shù):
-XX:+UseParallelOldGC使用Parallel Old收集器,設(shè)置該參數(shù)后,新生代Parallel+老年代Parallel Old。
CMS(Concurrent Mark Sweep)
CMS收集器(Concurrent Mark Sweep: 并發(fā)標(biāo)記清除)是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。
適合應(yīng)用在互聯(lián)網(wǎng)站或者B/S系統(tǒng)的服務(wù)器上,這類應(yīng)用尤其重視服務(wù)器的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短。
CMS非常適合堆內(nèi)存大、CPU核數(shù)多的服務(wù)器端應(yīng)用,也是G1出現(xiàn)之前大型應(yīng)用的首選收集器。
Concurrent Mark Sweep并發(fā)標(biāo)記清除,并發(fā)收集低停頓,并發(fā)指的是與用戶線程一起執(zhí)行。
啟該收集器的JVM參數(shù): -XX:+UseConcMarkSweepGC 開啟該參數(shù)后會(huì)自動(dòng)將-XX:+UseParNewGC打開
開啟該參數(shù)后,使用ParNew(Young區(qū)用) + CMS(Old區(qū)用) + Serial Old的收集器組合,Serial Old將作為CMS出錯(cuò)的后備收集器
CMS收集器的運(yùn)行過程分為下列4步:
**初始標(biāo)記:**標(biāo)記GC Roots能直接關(guān)聯(lián)的對(duì)象。速度很快但是存在Stop The World。
**并發(fā)標(biāo)記:**進(jìn)行GC Roots Tracing 的過程,找出存活對(duì)象且用戶線程可并發(fā)執(zhí)行。
**重新標(biāo)記:**為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄。仍然存在Stop The World問題。
**并發(fā)清除:**對(duì)標(biāo)記的對(duì)象進(jìn)行清除回收。
**CMS優(yōu)點(diǎn):**并發(fā)收集低停頓
缺點(diǎn):
1.浮動(dòng)垃圾:由于CMS并發(fā)清理階段用戶線程還在運(yùn)行著,伴隨程序運(yùn)行自然會(huì)有新垃圾產(chǎn)生,這部分垃圾得標(biāo)記過程之后,所以CMS無法在當(dāng)收集中處理掉他們,只好留待下一次GC清理掉,這一部分垃圾稱為浮動(dòng)垃圾。在jdk1.5默認(rèn)設(shè)置下,CMS收集器當(dāng)老年代使用了68%的空間就會(huì)被激活,可以通過-XX:CMSInitialOccupancyFraction的值來提高觸發(fā)百分比,在jdk1.6中CMS啟動(dòng)閾值提升到了92%,要是CMS運(yùn)行期間預(yù)留的內(nèi)存無法滿足程序的需要,就會(huì)出現(xiàn)”Concurrent Mode Failure“,然后降級(jí)臨時(shí)啟用Serial Old收集器進(jìn)行老年代的垃圾收集,這樣停頓時(shí)間就很長了。所以-XX:CMSInitialOccupancyFraction設(shè)置太高容易導(dǎo)致大量”Concurrent Mode Failure“。
2.有空間碎片:CMS是一款基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,所以會(huì)產(chǎn)生空間碎片。為了解決這個(gè)問題,CMS提供了-XX:UseCMSCompactAtFullCollection開發(fā)參數(shù)用于開啟內(nèi)存碎片的合并整理,由于內(nèi)存整理是無法并行的,所以停頓時(shí)間會(huì)變長。還有-XX:CMSFullGCBeforeCompaction,這個(gè)參數(shù)用于設(shè)置多少次不壓縮Full GC后,跟著來一次帶壓縮的(默認(rèn)為0)。
3.對(duì)CPU資源敏感。在并發(fā)標(biāo)記和并發(fā)清除階段雖然不會(huì)停止用戶線程,但是會(huì)因?yàn)檎加靡徊糠謈pu資源進(jìn)行垃圾回收導(dǎo)致用戶程序變慢。
CMS默認(rèn)啟動(dòng)的回收線程數(shù)是(cpu數(shù)量+3)/4。所以CPU數(shù)量少會(huì)導(dǎo)致用戶程序執(zhí)行速度降低較多。
G1收集器
G1適用于全堆,既可以在新生代使用和老年代使用。G1與之前的收集器有很大的不同,是從不同的角度去設(shè)計(jì)的。
回想下之前的垃圾收集器的特點(diǎn):
1.年輕代和老年代都是各自獨(dú)立的連續(xù)的內(nèi)存塊。
2.年輕代Eden+from+to使用復(fù)制算法
3.老年代的收集必須掃描全部老年代內(nèi)存空間。
4.都是以盡可能少而快速地執(zhí)行GC為設(shè)計(jì)原則
G1收集器的設(shè)計(jì)目標(biāo)是取代CMS收集器,它同CMS相比,在以下方面表現(xiàn)的更出色:
1、G1是一個(gè)有整理內(nèi)存過程的垃圾收集器,不會(huì)產(chǎn)生很多內(nèi)存碎片。
2、G1的Stop The World(STW)更可控,G1在停頓時(shí)間上添加了預(yù)測(cè)機(jī)制,用戶可以指定期望停頓時(shí)間。
CMS垃圾收集器雖然減少了暫停應(yīng)用程序的運(yùn)行時(shí)間,但是它還是存在著內(nèi)存碎片問題。于是,為了去除內(nèi)存碎片問題,同時(shí)又保留
CMS垃圾收集器低暫停時(shí)間的優(yōu)點(diǎn),JAVA7發(fā)布了一個(gè)新的垃圾收集器——G1垃圾收集器。
G1是在2012年才在jdk1.7u4中可用。 oracle官方計(jì)劃在jdk9中將G1變成默認(rèn)的垃圾收集器以替代CMS。它是一 款面向服務(wù)端應(yīng)用的收
器,主要應(yīng)用在多CPU和大內(nèi)存服務(wù)器環(huán)境下,極大的減少垃圾收集的停頓時(shí)間,全面提升服務(wù)器的性能,逐步替換java8以前的CM:
集器。
主要改變是Eden,Survivor和Tenured等內(nèi)存區(qū)域不再是連續(xù)的了,而是變成了一個(gè)個(gè)大小一樣的region ,每個(gè)region從1M到32M不等。- - 個(gè)region有可能屬于Eden, Survivor或 者Tenured內(nèi)存區(qū)域。
底層原理
G1的最大好處是化整為零,避免全內(nèi)存掃描,只需要按照區(qū)域來進(jìn)行掃描即可。
G1收集器大致可分為如下步驟:
**初始標(biāo)記:**僅標(biāo)記GC Roots能直接到的對(duì)象,并且修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序并發(fā)運(yùn)行時(shí),能在正確可用的Region中創(chuàng)建新對(duì)象。(需要線程停頓,但耗時(shí)很短。)
**并發(fā)標(biāo)記:**從GC Roots開始對(duì)堆中對(duì)象進(jìn)行可達(dá)性分析,找出存活對(duì)象。(耗時(shí)較長,但可與用戶程序并發(fā)執(zhí)行)
**最終標(biāo)記:**為了修正在并發(fā)標(biāo)記期間因用戶程序執(zhí)行而導(dǎo)致標(biāo)記產(chǎn)生變化的那一部分標(biāo)記記錄。且對(duì)象的變化記錄在線程Remembered Set Logs里面,把Remembered Set Logs里面的數(shù)據(jù)合并到Remembered Set中。(需要線程停頓,但可并行執(zhí)行。)
**篩選回收:**對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶所期望的GC停頓時(shí)間來制定回收計(jì)劃。(可并發(fā)執(zhí)行)
總結(jié)
到此這篇關(guān)于JVM的7種垃圾回收器(小結(jié))的文章就介紹到這了,更多相關(guān)JVM 垃圾回收器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot打包成Docker鏡像的幾種實(shí)現(xiàn)方式
Spring Boot是一個(gè)用于構(gòu)建獨(dú)立的、可執(zhí)行的Spring應(yīng)用程序的框架,結(jié)合使用Spring Boot和Docker,可以方便地將應(yīng)用程序部署到不同的環(huán)境中本文,主要介紹了SpringBoot打包成Docker鏡像的幾種實(shí)現(xiàn)方式,感興趣的可以了解一下2024-01-01淺談靜態(tài)變量、成員變量、局部變量三者的區(qū)別
下面小編就為大家?guī)硪黄獪\談靜態(tài)變量、成員變量、局部變量三者的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09詳解SpringBoot如何創(chuàng)建自定義Starter
Spring Boot的自動(dòng)配置機(jī)制為開發(fā)人員提供了一種輕松集成和配置各種功能的便捷方式,本文將深入探討在Spring Boot中如何創(chuàng)建自定義Starter,為構(gòu)建模塊化且易維護(hù)的應(yīng)用提供有力的支持,需要的朋友可以參考下2024-02-02java 中HashMap、HashSet、TreeMap、TreeSet判斷元素相同的幾種方法比較
這篇文章主要介紹了從源碼的角度淺析HashMap、TreeMap元素的存儲(chǔ)和獲取元素的邏輯;從Map與Set之間的關(guān)系淺析常用的Set中元素的存儲(chǔ)和判斷是否重復(fù)的邏輯,需要的朋友可以參考下2017-01-01JPA findById方法和getOne方法的區(qū)別說明
這篇文章主要介紹了JPA findById方法和getOne方法的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2021-08-08