1. 垃圾回收算法
GC和FGC的區(qū)別
次數(shù)上頻繁收集Young區(qū),次數(shù)上較少收集Old區(qū)毕泌,基本不動(dòng)元空間顿仇。GC(YGC)是指新生代的垃圾回收温数,GC很頻繁,因?yàn)榇蠖鄶?shù)的Java對(duì)象存活時(shí)間都很短憔披,所以GC的回收速度很快、也很頻繁。FGC是指養(yǎng)老區(qū)(Old)的垃圾回收芬膝,GC回收速度不頻繁望门,也不快,因?yàn)橐獟呙枵麄€(gè)老年區(qū)的空間锰霜,所以它的速度比GC慢10倍左右筹误。
GC的四大算法引用計(jì)數(shù)法、復(fù)制算法(Copying)癣缅、標(biāo)記清除(Mark-Swap)厨剪、標(biāo)記壓縮(Mark-Compact)
1.1 引用計(jì)數(shù)法
- JVM的實(shí)現(xiàn)一般不使用這種方法,應(yīng)用在微軟的COM/ActionScript3/Python...
- 有被引用這個(gè)對(duì)象友存,計(jì)數(shù)器就+1祷膳,沒(méi)有被引用的就被回收了
缺點(diǎn)如下: - 1.每次對(duì)對(duì)象進(jìn)行賦值的時(shí)候都要將計(jì)數(shù)器+1,且計(jì)數(shù)器本身也有消耗
- 2.較難處理循環(huán)引用(A引用B屡立,B引用A)
System.gc(); //手動(dòng)喚醒GC直晨,禁用手動(dòng)GC,JDK1.8的垃圾回收已經(jīng)很智能了
//不是立刻執(zhí)行膨俐,也是要看系統(tǒng)的調(diào)度勇皇,類(lèi)似創(chuàng)建了線程,也不是立刻執(zhí)行吟策,得看系統(tǒng)的調(diào)度
1.2 復(fù)制算法
- 新生代(Young)中使用的GC就是使用的復(fù)制算法儒士。
-XX:MaxTenuringThreshold //設(shè)置對(duì)象再新生代中存活代數(shù),默認(rèn)是15檩坚,最大也就是15着撩,因?yàn)閙arkword只留了4bit給其標(biāo)識(shí)
- 復(fù)制算法的基本思想是將內(nèi)存分為兩塊,每次只用其中一塊匾委,這塊內(nèi)存用完拖叙,就將或者的對(duì)象復(fù)制到另外一塊上去。復(fù)制算法不會(huì)產(chǎn)生內(nèi)存碎片赂乐,但是耗空間薯鳍。
- 從根集合(GCRoots)開(kāi)始,通過(guò)Tracing從FROM區(qū)找到存活對(duì)象挨措,拷貝到TO區(qū)中挖滤;FROM區(qū)和TO區(qū)交換,下次內(nèi)存分配繼續(xù)從TO開(kāi)始浅役。
1.3 標(biāo)記清除算法
- 思想是:先從內(nèi)存中標(biāo)記出來(lái)要回收的對(duì)象斩松,然后進(jìn)行統(tǒng)一回收。
- 標(biāo)記清除算法空間節(jié)約出來(lái)了觉既,但是會(huì)產(chǎn)生內(nèi)存碎片惧盹。而且要掃描兩次乳幸,一次標(biāo)記,一次清除钧椰,浪費(fèi)了時(shí)間粹断。
1.4 標(biāo)記壓縮算法
- 思想是:先從內(nèi)存中標(biāo)記出來(lái)要回收的對(duì)象,然后進(jìn)行統(tǒng)一回收嫡霞,之后對(duì)剩下來(lái)的存活的對(duì)象進(jìn)行整理(丟在同一側(cè))
- 缺點(diǎn):效率不高瓶埋,需要一定對(duì)象的成本,耗時(shí)比較嚴(yán)重诊沪。
- 改進(jìn):標(biāo)記-清除-壓縮算法:標(biāo)記清除和標(biāo)記壓縮算法的折中悬赏,進(jìn)行多次的GC后才壓縮。
JVM的GC用的是哪種方法娄徊?
新生代GC使用復(fù)制算法闽颇,在老年代FGC使用標(biāo)記壓縮、標(biāo)記清除算法
2. 垃圾回收算法和垃圾回收器的關(guān)系寄锐?
GC算法(引用計(jì)數(shù)/復(fù)制/標(biāo)記清除/標(biāo)記整理)是內(nèi)存回收的方法論兵多,垃圾回收器就是這些GC算法的落地實(shí)現(xiàn)。到目前為止還沒(méi)有完美的垃圾回收器出現(xiàn)橄仆,更加沒(méi)有萬(wàn)能的收集器剩膘,只是針對(duì)不同場(chǎng)合選用最合適的垃圾收集器。
3. 主要的垃圾收集器
參數(shù) | 新生代收集器 | 新生代算法 | 老年代收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC | Serial | 復(fù)制 | SerialOldGC | 標(biāo)整 |
-XX:+UseParNewGC | ParNew | 復(fù)制 | SerialOldGC | 標(biāo)整 |
-XX:+UseParallelGC或-XX:UseParallelOldGC | Parallel(Scavenge) | 復(fù)制 | ParallelOldGC | 標(biāo)整 |
-XX:UseConcMarkSweepGC | ParNew | 復(fù)制 | CMS+Serial Old(SerialOld為CMS出錯(cuò)的后備) | 標(biāo)清 |
-XX:UseG1GC | 整體上使用標(biāo)整算法 | 局部是通過(guò)復(fù)制算法盆顾,不會(huì)產(chǎn)生內(nèi)存碎片 |
3.1 Serial-串行收集器
為單線程設(shè)計(jì)并且只使用一個(gè)線程進(jìn)行垃圾回收怠褐,會(huì)暫停所有的用戶線程,不適合用在服務(wù)器環(huán)境您宪,運(yùn)行在Client模式下的JVM是個(gè)不錯(cuò)的選擇奈懒,在用戶的桌面應(yīng)用場(chǎng)景中使用串行收集器也是可以接受的。
3.2 Parallel-并行收集器
是Serial的多線程版本宪巨,和Serial共享很多源碼磷杏,多個(gè)垃圾收集線程并行工作,此時(shí)用戶線程是暫停的捏卓,停頓時(shí)間比Serial短极祸,效率更高適合用于科學(xué)計(jì)算/大數(shù)據(jù)處理等弱交互場(chǎng)景。在單核CPU下怠晴,可能并行收集器比串行收集器還慢遥金。
3.3 CMS-標(biāo)記并發(fā)清除-ConcMarkSweep)
用戶線程和垃圾收集線程同時(shí)執(zhí)行(不一定是并行,可能交替執(zhí)行)蒜田,不需要停頓用戶線程稿械,互聯(lián)網(wǎng)公司多用它,適合對(duì)響應(yīng)時(shí)間有要求的場(chǎng)景物邑。(有一段還是會(huì)暫停溜哮,但是比較短),在2020年3月的jdk14中CMS被刪除色解。
3.4 G1
G1垃圾回收器將堆內(nèi)存分成不同的區(qū)域茂嗓,然后并發(fā)的對(duì)其進(jìn)行垃圾的回收。
4.怎么查看服務(wù)器的垃圾回收器科阎?
使用java -XX:+PrintCommandLineFlags -version
查看初始配置或者jinfo -flag PrintCommandLineFlags pid
查看具體的應(yīng)用程序的參數(shù)述吸。Java8默認(rèn)使用的是Parallel,即并行垃圾回收锣笨。
5. 垃圾收集器的使用
5.1 Serial(新生代)+SerialOld(養(yǎng)老區(qū))
開(kāi)啟配置的參數(shù)-XX:+UseSerialGC
蝌矛,整個(gè)過(guò)程都會(huì)停掉用戶線程(STW)。
5.2 ParNew(新生代)+SerialOld(養(yǎng)老區(qū))
新生代采用復(fù)制算法(多個(gè)線程)错英,暫停所有用戶線程(STW)入撒,老年區(qū)采用標(biāo)記整理算法(單個(gè)線程),暫停所有用戶線程(STW)椭岩。 開(kāi)啟配置的參數(shù)-XX:+UseParNewGC
茅逮,可以使用-XX:+ParallelGCThreads
限制線程數(shù),默認(rèn)開(kāi)啟和CPU核心相同的線程數(shù)判哥,但是ParNewGC這種默認(rèn)搭配的方式已經(jīng)不推薦使用了献雅。
5.3 ParNew(新生代)+CMS(養(yǎng)老區(qū))
使用-XX:+UseConcMarkSweep
開(kāi)啟CMS收集器
//CMS-并發(fā)標(biāo)記清除,是一種以獲得最短回收停頓時(shí)間為目標(biāo)的收集器塌计,適合互聯(lián)網(wǎng)網(wǎng)站和B/S的服務(wù)器上挺身,這類(lèi)應(yīng)用注重服務(wù)器的響應(yīng)速度,希望停頓時(shí)間最短
//CMS非常適合堆內(nèi)存大锌仅、CPU核數(shù)多的服務(wù)端應(yīng)用章钾,也是G1出現(xiàn)之前大型應(yīng)用的首選收集器
//開(kāi)啟之后會(huì)默認(rèn)打開(kāi)ParNew,在新生代中使用的收集器(為什么不整合ParallelScavenge?因?yàn)榈讓硬荒芗嫒?
//在垃圾收集階段用戶線程沒(méi)有中斷热芹,所以在CMS運(yùn)行過(guò)程中伍玖,還應(yīng)該保證用戶線程有足夠的內(nèi)存可用。
//不能像其他收集器一樣等到老年區(qū)滿了才回收剿吻,而是應(yīng)該設(shè)定一個(gè)閾值窍箍,便開(kāi)始回收,以確保在垃圾回收的過(guò)程中用戶程序有足夠的空間運(yùn)行丽旅。
//要是CMS運(yùn)行期間預(yù)留的內(nèi)存不夠程序運(yùn)行的需要椰棘,還會(huì)開(kāi)啟SerialOld收集器,作為CMS出錯(cuò)的后備收集器
//CMS的四個(gè)步驟:
//1.初始化標(biāo)記(CMS initial mark)榄笙,會(huì)停掉用戶線程(STE)
//2.并發(fā)標(biāo)記(CMS concurrent mark)邪狞,不會(huì)停掉用戶線程,會(huì)和用戶線程一起進(jìn)行
//3.重新標(biāo)記(CMS remark)茅撞,用來(lái)標(biāo)記出來(lái)之前標(biāo)記了帆卓,但是現(xiàn)在還在使用的一些對(duì)象巨朦,會(huì)停掉用戶線程(STW)
//4.并發(fā)清除(CMS sweep),和用戶線程一起進(jìn)行剑令,清除GCRoots不可達(dá)對(duì)象
CMS的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):并發(fā)收集停頓低
缺點(diǎn):并發(fā)執(zhí)行糊啡,對(duì)CPU壓力大,采用的標(biāo)記清除算法會(huì)產(chǎn)生內(nèi)存碎片
既然CMS使用標(biāo)記壓縮算法會(huì)產(chǎn)生內(nèi)存碎片吁津,那為什么還要使用標(biāo)記壓縮算法而不使用標(biāo)記整理算法棚蓄?因?yàn)?strong>CMS的清理過(guò)程和用戶線程的執(zhí)行是在一起進(jìn)行的,如果你把對(duì)象整理了碍脏,對(duì)象的內(nèi)存位置發(fā)生改變梭依,用戶線程就不能正常執(zhí)行了。標(biāo)整算法適合使用在STW情況下典尾。
5.4 Parallel Scavenge(新生代)+ParallelOld(養(yǎng)老區(qū))
JDK1.8默認(rèn)使用役拴,關(guān)注吞吐量,和ParNew的區(qū)別在于ParallelScavenge有自適應(yīng)調(diào)節(jié)機(jī)制:JVM會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息钾埂,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間(-XX:+MaxGCPauseMills)
或最大的吞吐量扎狱。
//關(guān)注吞吐量,和ParNew的區(qū)別在于ParallelScavenge有自適應(yīng)調(diào)節(jié)機(jī)制:JVM會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息勃教,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間(-XX:+MaxGCPauseMills)或最大的吞吐量淤击。
//使用-XX:+UseParallelGC或-XX:+UseParallelOldGC可以互相激活去使用Parallel Scavenge收集器。
//可以指定GC的線程故源,使用-XX:ParallelGCThreads=N去調(diào)整污抬,CPU>8,N=5/8绳军,CPU<8印机,N=實(shí)際個(gè)數(shù)
//ParallelScanvenge和ParallelOld使用的是兩套算法,完全不一樣
//適合在后臺(tái)運(yùn)行而不需要太多交互任務(wù)的任務(wù)门驾,比如科學(xué)計(jì)算射赛、批量處理。也是Stop the world奶是。
//常見(jiàn)在服務(wù)器環(huán)境下使用
5.5 G1(新生代+養(yǎng)老區(qū))
使用參數(shù)-XX:+UseG1GC
開(kāi)啟G1收集器楣责,在Young區(qū)和Old區(qū)都能使用,使用G1之后聂沙,Heap只有兩層秆麸,region/Metaspace,以前的Young和Old都包含在region中及汉。
以前的垃圾收集器的特點(diǎn)
//以前的收集器的特點(diǎn):
//1.Young區(qū)和Old區(qū)是各自獨(dú)立并且連續(xù)的內(nèi)存塊
//2.年輕代收集使用Eden+S0+S1進(jìn)行復(fù)制算法
//3.老年代收集必須掃描整個(gè)老年代區(qū)域
//4.都是以盡量少而快速地執(zhí)行GC為設(shè)計(jì)原則
G1的特點(diǎn)
//G1的特點(diǎn):
//像CMS一樣沮趣,可以與應(yīng)用程序線程并發(fā)進(jìn)行
//整理空閑的空間更快
//需要更多時(shí)間來(lái)預(yù)測(cè)GC的停頓時(shí)間
//不希望犧牲大量的吞吐性能
//不需要更大的Java Heap
//1.利用多CPU、多核的硬件優(yōu)勢(shì)坷随,盡量縮短STW房铭。
//2.整體采用標(biāo)記整理算法驻龟,局部采用復(fù)制算法,不會(huì)產(chǎn)生內(nèi)存碎片
//3.G1不再劃分Eden缸匪、S0翁狐、S1,改成一個(gè)個(gè)的region
//4.G1收集器里面整個(gè)內(nèi)存都混合在一起了豪嗽,但是本身小范圍內(nèi)是存在Young和Old的區(qū)分,保留了新生代核老年代豌骏。
// 但他們不再是物理隔離的而是一部分region的集合并且不需要region是連續(xù)的龟梦,也就是說(shuō)依然會(huì)采用不同的GC方式來(lái)處理不同的區(qū)域。
//5.G1也是分代收集器窃躲,但是在整個(gè)內(nèi)存分區(qū)不存在物理上的年輕代和老年代的區(qū)別计贰,也不需要獨(dú)立的To區(qū)來(lái)做復(fù)制的準(zhǔn)備
// G1只有邏輯上的分代概念,每個(gè)分區(qū)都可能隨G1的運(yùn)行在不同代之間切換
//G1(Garbage-First)收集器是一款面向服務(wù)端應(yīng)用的收集器蒂窒,聚焦于多處理器和大容量的內(nèi)存中躁倒。
//在實(shí)現(xiàn)高吞吐量的同時(shí),盡可能地滿足垃圾暫停時(shí)間的要求
//G1的目的是為了取代CMS的收集器洒琢,它同CMS相比秧秉,在以下方面比較出色:
//1.G1是一個(gè)有整理內(nèi)存過(guò)程的垃圾收集器,不會(huì)產(chǎn)生很多內(nèi)存碎片
//2.G1的Stop the World(STW)更短衰抑,G1在停頓時(shí)間上添加了預(yù)測(cè)機(jī)制象迎,**用戶可以指定停頓的時(shí)間**
//在jdk9中將G1變成默認(rèn)的垃圾回收器以替代CMS
//G1的主要改變是Eden、Survivor和Tenured不再連續(xù)了呛踊,而是被分成了大小一樣的region砾淌。
//每個(gè)region從1M-32M不等,一個(gè)region有可能屬于Eden谭网、Survivor或者是Tenured
//在堆的使用上汪厨,G1并不要求對(duì)象的存儲(chǔ)一定是物理上的連續(xù),只需要邏輯上連續(xù)即可愉择。
//啟動(dòng)時(shí)可以通過(guò)參數(shù)-XX:G1HeapRegionSize=n指定region分區(qū)的大薪俾摇(1-32M,且必須是1<<k)锥涕,默認(rèn)將堆分為2048個(gè)分區(qū)
//也就是說(shuō)最大支持內(nèi)存為32M*2048=64G
G1收集器的過(guò)程
//G1步驟(和CMS類(lèi)似)
//1.初始化標(biāo)記(CMS initial mark)要拂,會(huì)停掉用戶線程
//2.并發(fā)標(biāo)記(CMS concurrent mark),不會(huì)停掉用戶線程站楚,會(huì)和用戶線程一起進(jìn)行
//3.最終標(biāo)記(CMS remark)脱惰,用來(lái)標(biāo)記出來(lái)之前標(biāo)記了,但是現(xiàn)在還在使用的一些對(duì)象窿春,會(huì)停掉用戶線程
//4.篩選回收拉一,和用戶線程一起進(jìn)行采盒,根據(jù)時(shí)間來(lái)進(jìn)行價(jià)值最大化的回收
更多參數(shù):
//-XX:+UseG1GC
//-XX:G1HeapRegionSize=n //Region區(qū)域大小,1-32M蔚润,且是1<<k
//-XX:MaxGCPauseMills=n //最大停頓時(shí)間磅氨,JVM盡可能停頓小于這個(gè)時(shí)間
//-XX:InitiatingHeapOccupancyPercent 堆占用多少就啟用GC,默認(rèn)為45(%)
//-XX:ConcGCThreads=n 并發(fā)GC使用的線程數(shù)量
//-XX:G1ReservePercent=n 空閑空間的預(yù)留百分比嫡纠,以降低目標(biāo)空間溢出的風(fēng)險(xiǎn)烦租,默認(rèn)是10(%)
5.6 如何選擇合適的回收器?
//單CPU或者小內(nèi)存除盏,單機(jī)程序
-XX:+UseSerialGC
//多CPU叉橱,需要最大吞吐量,比如后臺(tái)計(jì)算型的應(yīng)用
-XX:+UseParallelGC或-XX:+UseParallelOldGC
//多CPU者蠕,追求最低的停頓窃祝,虛快速響應(yīng),如互聯(lián)網(wǎng)應(yīng)用
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC