一、對(duì)象存活統(tǒng)計(jì)算法
1.引用計(jì)數(shù)器法
每當(dāng)對(duì)象被引用一次時(shí)劣砍,計(jì)數(shù)器的值就加一惧蛹;當(dāng)引用失效時(shí),計(jì)數(shù)器的值就減一刑枝。在任何時(shí)刻香嗓,計(jì)數(shù)器為0的值時(shí)不會(huì)被使用的。
優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單装畅,判定效率高靠娱。
缺點(diǎn):無(wú)法解決對(duì)象之間相互調(diào)用的問(wèn)題。
publicclassReferenceCountingGC{
publicObject instance =null;publicstaticvoidtestGC(){
ReferenceCountingGC objA =newReferenceCountingGC();
ReferenceCountingGC objB =newReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA =null;
objB =null;
System.gc();
}
}
上面代碼在gc之后objA和objB會(huì)回收掠兄,所以虛擬機(jī)并沒(méi)有使用引用計(jì)數(shù)法像云。
2.可達(dá)分析計(jì)算
該方法以一系列“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索蚂夕,搜索走過(guò)的路徑稱為引用鏈迅诬,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈條時(shí),則證明對(duì)象是不可用的婿牍。在java中侈贷,可以作為GC Roots的對(duì)象包括:
虛擬機(jī)棧中引用的對(duì)象。
方法區(qū)中靜態(tài)屬性引用的對(duì)象等脂。
方法區(qū)中常量引用的對(duì)象
本地方法棧中引用的對(duì)象
二俏蛮、對(duì)象的死亡過(guò)程
處在可達(dá)性分型中不可達(dá)的對(duì)象,暫時(shí)處于緩刑階段上遥。具體的死亡過(guò)程如下:
其中搏屑,判斷finalize()方法是否 需要執(zhí)行的標(biāo)準(zhǔn)有兩個(gè):
finalize()方法有沒(méi)有被重寫(xiě),沒(méi)被重寫(xiě)不調(diào)用
finalize()方法已經(jīng)執(zhí)行過(guò)粉楚,不調(diào)用辣恋。
換句話說(shuō),一個(gè)對(duì)象只有重寫(xiě)了finalize()方法并且第一個(gè)執(zhí)行,虛擬機(jī)才會(huì)執(zhí)行該對(duì)象的finalize()方法抑党。
三包警、垃圾收集算法
1.標(biāo)記-清除算法
標(biāo)記清除算法是最基礎(chǔ)的算法,此算法分為兩個(gè)階段標(biāo)記和清除
標(biāo)記:標(biāo)記需要回收的對(duì)象
清除:統(tǒng)一回收被標(biāo)記的對(duì)象底靠。
缺點(diǎn):效率低害晦,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高。清除后會(huì)產(chǎn)生大量的碎片空間暑中,在分配大對(duì)象時(shí)碎片空間無(wú)法利用會(huì)觸發(fā)另一次垃圾回收壹瘟。
2.復(fù)制算法
復(fù)制算法講內(nèi)存分為大小相同的兩塊,當(dāng)一塊使用完了之后將其中存活的對(duì)象復(fù)制到另一塊上面鳄逾。然后將之前的內(nèi)存空間整個(gè)清理掉稻轨。復(fù)制算法的具體過(guò)程分為
標(biāo)記:區(qū)分存活和需要收集的對(duì)象
復(fù)制:將存活的對(duì)象諸葛復(fù)制到新的空間
清理:將就得空間一次性清理掉
優(yōu)點(diǎn):復(fù)制后新空間和舊空間沒(méi)有內(nèi)存碎片。新對(duì)象的分配簡(jiǎn)單高效雕凹,只要一動(dòng)指針即可殴俱。
缺點(diǎn): 代價(jià)高昂,可用內(nèi)存縮小為了原來(lái)的一半枚抵。
復(fù)制算法的變種:
人們發(fā)現(xiàn)在新生代區(qū)使用復(fù)制算法時(shí)线欲,死亡對(duì)象的比例約為98%,即每次需要復(fù)制的存活對(duì)象大約只有2%汽摹,這樣就不需要將原內(nèi)存劃分為兩塊1:1的內(nèi)存李丰,而是根據(jù)新生代的特點(diǎn)分成8:1:1的三塊內(nèi)存,eden區(qū)占8逼泣,兩塊survivor分別占1趴泌。每次使用eden和一塊survivor,當(dāng)回收后將eden'和survivor存活的對(duì)象復(fù)制到另一塊survivor上拉庶,這樣講復(fù)制算法原來(lái)的浪費(fèi)一半的內(nèi)存空間壓縮到10%嗜憔。但是這樣并不總是保險(xiǎn)的,即有時(shí)候存活的對(duì)象會(huì)超過(guò)10%砍的,這時(shí)另一塊survivor空間就會(huì)放不下痹筛。這時(shí)jvm的做法是向老年代借一塊空間來(lái)存放存活對(duì)象莺治。這叫做分配擔(dān)保廓鞠,即當(dāng)survivor空間不夠時(shí)由老年代擔(dān)保可以將不夠的空間從老年代劃出使用谣旁。
3.標(biāo)記-整理算法
前面的復(fù)制算法適用于存活對(duì)象較少的區(qū)域床佳,如果存活對(duì)象較多時(shí)存活空間劃分較小會(huì)頻繁觸發(fā)分配擔(dān)保,這樣可能會(huì)影響到擔(dān)遍螅空間的正常使用砌们,如果擔(dān)保空間不夠用觸發(fā)full GC會(huì)使垃圾收集的效率大大降低。
標(biāo)記-整理算法時(shí)適用于老年代的算法浪感。需要兩個(gè)步驟
標(biāo)記:標(biāo)記存活對(duì)象
整理:將存活對(duì)象移動(dòng)到空間的一端昔头,將端邊界以外的空間都清理掉。移動(dòng)的時(shí)候不管要清理的對(duì)象影兽。直接讓存活對(duì)象覆蓋揭斧。之后整體清理。
四峻堰、Hotspot的算法實(shí)現(xiàn)
1.枚舉根節(jié)點(diǎn)
在根節(jié)點(diǎn)枚舉中讹开,GCRoots的主要節(jié)點(diǎn)在全局性的引用和執(zhí)行上下文中,如果逐個(gè)檢查會(huì)很消耗時(shí)間捐名。
另外可達(dá)性分析要求執(zhí)行系統(tǒng)必須凍結(jié)在某個(gè)時(shí)間點(diǎn)上旦万,不可以出現(xiàn)分析過(guò)程中引用關(guān)系還在出現(xiàn)不斷變化的過(guò)程中。
HotSpot的準(zhǔn)確式GC
HotSpot采用了準(zhǔn)確式GC以提升GC roots的枚舉速度镶蹋。所謂準(zhǔn)確式GC成艘,就是讓JVM知道內(nèi)存中某位置數(shù)據(jù)的類型什么。比如當(dāng)前內(nèi)存位置中的數(shù)據(jù)究竟是一個(gè)整型變量還是一個(gè)引用類型贺归。這樣JVM可以很快確定所有引用類型的位置狰腌,從而更有針對(duì)性的進(jìn)行GC roots枚舉。HotSpot是利用OopMap來(lái)實(shí)現(xiàn)準(zhǔn)確式GC的牧氮。當(dāng)類加載完成后琼腔,HotSpot 就將對(duì)象內(nèi)存布局之中什么偏移量上數(shù)值是一個(gè)什么樣的類型的數(shù)據(jù)這些信息存放到 OopMap 中;在 HotSpot 的 JIT 編譯過(guò)程中踱葛,同樣會(huì)插入相關(guān)指令來(lái)標(biāo)明哪些位置存放的是對(duì)象引用等丹莲,這樣在 GC 發(fā)生時(shí),HotSpot 就可以直接掃描 OopMap 來(lái)獲取對(duì)象引用的存儲(chǔ)位置尸诽,從而進(jìn)行 GC Roots 枚舉甥材。
2.serial收集器
是最古老的也是最基礎(chǔ)的收集器,單線程是因?yàn)楫?dāng)他在進(jìn)行工作時(shí)必須停止其他活動(dòng)的線程
優(yōu)點(diǎn):簡(jiǎn)單高效性含,沒(méi)有線程切換帶來(lái)的開(kāi)銷洲赵。在個(gè)人桌面管理中,stop the world的時(shí)間一般能控制在幾十毫秒商蕴,在這時(shí)使用serial收集器是很好的選擇叠萍。
3.ParNew收集器
parNew是serial的多線程版本,注意在parnew工作時(shí)仍然會(huì)停止其他工作線程绪商。不同的是相比于serial苛谷,parnew采用多線程的方式回收垃圾。在多核cpu時(shí)收集速度會(huì)明顯的高于serial格郁。但是在單核或者核心較少的機(jī)器中腹殿,serial會(huì)高于parnew的收集速度独悴。
4.parallel scavenge收集器
是一個(gè)新生代收集器,使用復(fù)制算法锣尉,并行收集刻炒。
特點(diǎn):關(guān)注吞吐量。吞吐量=客戶代碼運(yùn)行時(shí)間/(客戶代碼運(yùn)行時(shí)間+垃圾收集時(shí)間)
兩個(gè)重要的參數(shù):
MaxGCPauseMillis:最大垃圾收集停頓時(shí)間自沧。太大垃圾收集停頓時(shí)間明顯落蝙,影響用戶體驗(yàn)。太小導(dǎo)致垃圾收集不完整暂幼,容易觸發(fā)頻繁的GC筏勒,降低吞吐量。
GCTimeRatio:垃圾收集時(shí)間占比旺嬉。計(jì)算方式為:用戶代碼運(yùn)行時(shí)間/垃圾收集時(shí)間管行。如19 = 19:1,實(shí)際垃圾收集時(shí)間為1:(19+1) = 5%
5.serial old收集器
serial old類似于serial收集器邪媳,serial一般使用于新生代捐顷,serial old適用于老年代。采用的收集算法是標(biāo)記-整理雨效。主要的使用場(chǎng)景:
主要使用于client模式下的虛擬機(jī)
在server模式下迅涮,當(dāng)CMS并發(fā)收集失敗時(shí),作為CMS的后備方案徽龟。
6.parallel old收集器
parallel old是parallel scavenge的老年代版本叮姑。采用標(biāo)記-整理算法。
主要使用場(chǎng)景為配合新生代paralle scavenge使用据悔,使得“吞吐量?jī)?yōu)先的概念”成為名副其實(shí)传透。在paralle old出現(xiàn)之前,parallel scavenge只能和serial old配合极颓,由于serial old是單線程的版本朱盐,在多核cpu中能力有限,使得新生代的parallen scavenge的“吞吐量?jī)?yōu)先”效果有限菠隆。
7.CMS(cocurrent mark sweep)收集器
是并發(fā)清除收集器兵琳,強(qiáng)調(diào)以最短的收集停頓時(shí)間為目的。相比于前幾個(gè)收集器骇径,CMS比較復(fù)雜躯肌。整個(gè)過(guò)程分為4個(gè)步驟:
初始標(biāo)記:僅僅標(biāo)記GC Roots能直接關(guān)聯(lián)到的對(duì)象。
并發(fā)標(biāo)記:進(jìn)行GC Roots Tracing過(guò)程既峡。
重新標(biāo)記:修正并發(fā)期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變化的一部分對(duì)象的標(biāo)記記錄
并發(fā)清除:最后多線程清除已標(biāo)記兩次的對(duì)象羡榴。
其中初始標(biāo)記和和重新標(biāo)記兩個(gè)過(guò)程需要stop the world碧查。最耗時(shí)的是并發(fā)標(biāo)記和并發(fā)整理运敢,由于這兩個(gè)過(guò)程是與用戶線程并發(fā)進(jìn)行的所以總體來(lái)說(shuō)CMS可以說(shuō)成是并行的垃圾收集器校仑。除了以上優(yōu)點(diǎn),CMS的缺點(diǎn)如下:
缺點(diǎn):
CMS對(duì)CPU資源非常敏感传惠。CMS默認(rèn)的回收線程數(shù)為(cpu核心+3)/4迄沫,看以看到當(dāng)cpu核心數(shù)增多時(shí)CMS線程的變化趨勢(shì)為 1->四份之一的總核心數(shù)。即當(dāng)cpu越多時(shí)CMS占用的資源越少卦方。當(dāng)cpu核心越少羊瘩,CMS占用的資源越多,嚴(yán)重影響用戶代碼的執(zhí)行盼砍。分析這種情況尘吗,當(dāng)運(yùn)行并發(fā)標(biāo)記和并發(fā)清除時(shí),CMS線程獨(dú)占CPU浇坐,使得用戶線程停滯或者分配到其他CPU睬捶,此時(shí)用戶代碼執(zhí)行速度大大降低。在這種情況下提出了i-CMS近刘,改變點(diǎn)就是在CMS執(zhí)行并發(fā)標(biāo)和并發(fā)清除時(shí)和用戶線程交替運(yùn)行擒贸,此時(shí)的CMS線程是并行收集嗎?(未確定)觉渴,這樣整個(gè)垃圾收集的過(guò)程會(huì)拉長(zhǎng)介劫,但是用戶代碼執(zhí)行效率提高了。實(shí)踐證明不好用案淋。
并發(fā)清除時(shí)用戶線程會(huì)產(chǎn)生新的垃圾座韵,這些垃圾稱為浮動(dòng)垃圾。由于并發(fā)性踢京,所以需要在清除時(shí)需要額外的空間給用戶線程使用回右。所以CMS一般需要預(yù)留一塊內(nèi)存,而垃圾收集也并不是在老年代填滿了才進(jìn)行漱挚。使用CMSInitiatingOccupancyFraction控制觸發(fā)垃圾回收的內(nèi)存占比翔烁。當(dāng)調(diào)的太高時(shí),可能預(yù)留空間會(huì)無(wú)法滿足浮動(dòng)垃圾的使用從而產(chǎn)生Cocurrent Mode Failure旨涝。此時(shí)采用serial old收集器蹬屹。調(diào)的太小則硬件浪費(fèi)嚴(yán)重。
CMS的收集算法是標(biāo)記-清除白华,所以會(huì)空間碎片慨默。如果大對(duì)象無(wú)法分配內(nèi)存則會(huì)觸發(fā)Full GC 。為了解決這個(gè)問(wèn)題弧腥,使用-XX:+UseCMSCompactAtFullCollection參數(shù)控制在CMS收集完后進(jìn)行空間整理厦取。由于內(nèi)存整理無(wú)法并發(fā),所以在整理時(shí)會(huì)停掉用戶線程管搪,增加停頓時(shí)間虾攻。因此在此基礎(chǔ)上增加了-XX:CMSFullGCsBeforeCompaction來(lái)控制執(zhí)行n次不壓縮的CMS后來(lái)一次整理空間的CMS铡买。