JVM-05垃圾收集Garbage Collection(中)【垃圾收集算法】
文章目錄
- 思維導(dǎo)圖
- 標(biāo)記清除算法(其他算法的基礎(chǔ))
- 復(fù)制算法(新生代的GC)
- 標(biāo)記整理/壓縮算法(老年代的GC)
- 標(biāo)記-清除算法、復(fù)制算法、標(biāo)記整理算法總結(jié)
- 分代收集算法(新生代的GC+老年代的GC)
思維導(dǎo)圖
JVM-04垃圾收集Garbage Collection(上)【垃圾對(duì)象的判定】探討了如何判定堆內(nèi)存中的對(duì)象是否已經(jīng)死亡,這里我們來(lái)繼續(xù)討論下JVM中常用的垃圾收集算法
標(biāo)記清除算法(其他算法的基礎(chǔ))
標(biāo)記-清除算法是現(xiàn)代垃圾回收算法的思想基礎(chǔ)。標(biāo)記-清除算法將垃圾回收分為兩個(gè)階段:標(biāo)記階段和清除階段。一種可行的實(shí)現(xiàn)是,在標(biāo)記階段,首先通過(guò)根節(jié)點(diǎn),標(biāo)記所有從根節(jié)點(diǎn)開(kāi)始的可達(dá)對(duì)象。因此,未被標(biāo)記的對(duì)象就是未被引用的垃圾對(duì)象;然后,在清除階段,清除所有未被標(biāo)記的對(duì)象。
當(dāng)堆中的有效內(nèi)存空間(available memory)被耗盡的時(shí)候,就會(huì)停止整個(gè)程序(也被成為stop the world),然后進(jìn)行兩項(xiàng)工作,第一項(xiàng)則是標(biāo)記,第二項(xiàng)則是清除
標(biāo)記:遍歷所有的GC Roots,并將從GC Roots可達(dá)的對(duì)象設(shè)置為存活對(duì)象;
清除:遍歷堆中的所有對(duì)象,將沒(méi)有被標(biāo)記可達(dá)的對(duì)象清除;
也就是說(shuō),就是當(dāng)程序運(yùn)行期間,若可以使用的內(nèi)存被耗盡的時(shí)候,GC線程就會(huì)被觸發(fā)并將程序暫停,隨后將依舊存活的對(duì)象標(biāo)記一遍,最終再將堆中所有沒(méi)被標(biāo)記的對(duì)象全部清除掉,接下來(lái)便讓程序恢復(fù)運(yùn)行。
優(yōu)缺點(diǎn):
- 涉及大量的內(nèi)存遍歷工作,所以執(zhí)行性能較低,這也會(huì)導(dǎo)致“stop the world”時(shí)間較長(zhǎng),java程序吞吐量降低;
- 對(duì)象被清除之后,被清除的對(duì)象留下內(nèi)存的空缺位置,造成內(nèi)存不連續(xù),空間浪費(fèi)。
復(fù)制算法(新生代的GC)
將現(xiàn)有的內(nèi)存空間分為兩快,每次只使用其中一塊,在垃圾回收時(shí)將正在使用的內(nèi)存中的存活對(duì)象復(fù)制到未被使用的內(nèi)存塊中,之后,清除正在使用的內(nèi)存塊中的所有對(duì)象,交換兩個(gè)內(nèi)存的角色,完成垃圾回收。
Java 堆內(nèi)存并不需要按照1:1的比例劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中的一塊Survivor。當(dāng)回收時(shí),將Eden和Survivor中還存活著的對(duì)象一次性地拷貝到另外一個(gè)Survivor空間上,最后清理掉Eden和剛才用過(guò)的Survivor的空間。
HotSpot虛擬機(jī)默認(rèn)Eden:Survivor From ::Survivor To 的大小比例是8:1:1(可以通過(guò)-SurvivorRattio來(lái)配置),也就是每次新生代中可用內(nèi)存空間為整個(gè)新生代容量的90%,只有10%的內(nèi)存會(huì)被“浪費(fèi)”。
當(dāng)然,98%的對(duì)象可回收只是一般場(chǎng)景下的數(shù)據(jù),我們沒(méi)有辦法保證回收都只有不多于10%的對(duì)象存活,當(dāng)Survivor空間不夠用時(shí),需要依賴(lài)其他內(nèi)存(這里指老年代)進(jìn)行分配擔(dān)保。
優(yōu)缺點(diǎn):
復(fù)制算法相對(duì)標(biāo)記壓縮算法來(lái)說(shuō)更簡(jiǎn)潔高效,但它的缺點(diǎn)也顯而易見(jiàn),它不適合用于存活對(duì)象多的情況,因?yàn)槟菢有枰獜?fù)制的對(duì)象很多,復(fù)制性能較差,所以復(fù)制算法往往用于內(nèi)存空間中新生代的垃圾回收,因?yàn)樾律写婊顚?duì)象較少,復(fù)制成本較低。它另外一個(gè)缺點(diǎn)是內(nèi)存空間占用成本高,因?yàn)樗趦煞輧?nèi)存空間做對(duì)象復(fù)制,在非垃圾回收的周期內(nèi)只用到了一份內(nèi)存空間,內(nèi)存利用率較低。
標(biāo)記整理/壓縮算法(老年代的GC)
標(biāo)記:它的第一個(gè)階段與標(biāo)記/清除算法是一模一樣的,均是遍歷GC Roots,然后將存活的對(duì)象標(biāo)記。
整理:移動(dòng)所有存活的對(duì)象,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。因此,第二階段才稱(chēng)為整理階段。
上圖中可以看到,標(biāo)記的存活對(duì)象將會(huì)被整理,按照內(nèi)存地址依次排列,而未被標(biāo)記的內(nèi)存會(huì)被清理掉。如此一來(lái),當(dāng)我們需要給新對(duì)象分配內(nèi)存時(shí),JVM只需要持有一個(gè)內(nèi)存的起始地址即可,這比維護(hù)一個(gè)空閑列表顯然少了許多開(kāi)銷(xiāo)。
優(yōu)點(diǎn):
因?yàn)闃?biāo)記清除算法會(huì)造成內(nèi)存的不連續(xù),所以標(biāo)記整理(壓縮)算在標(biāo)記清除算法的基礎(chǔ)上,增加了整理過(guò)程,解決了標(biāo)記清除算法內(nèi)存不連續(xù)的問(wèn)題。同時(shí)也消除了復(fù)制算法當(dāng)中,內(nèi)存減半的高額代價(jià)。
缺點(diǎn):
標(biāo)記整理(壓縮)也會(huì)產(chǎn)生“stop the world”,不能和java程序并發(fā)執(zhí)行。在壓縮過(guò)程中一些對(duì)象內(nèi)存地址會(huì)發(fā)生改變,java程序只能等待壓縮完成后才能繼續(xù)。
標(biāo)記-清除算法、復(fù)制算法、標(biāo)記整理算法總結(jié)
在GC線程開(kāi)啟時(shí),或者說(shuō)GC過(guò)程開(kāi)始時(shí),它們都要暫停應(yīng)用程序(stop the world)。
它們的區(qū)別如下:(>表示前者要優(yōu)于后者,=表示兩者效果一樣)
(1)效率:復(fù)制算法>標(biāo)記/整理算法>標(biāo)記/清除算法(此處的效率只是簡(jiǎn)單的對(duì)比時(shí)間復(fù)雜度,實(shí)際情況不一定如此)。
(2)內(nèi)存整齊度:復(fù)制算法=標(biāo)記/整理算法>標(biāo)記/清除算法。
(3)內(nèi)存利用率:標(biāo)記/整理算法=標(biāo)記/清除算法>復(fù)制算法。
- 可以看到標(biāo)記/清除算法是比較落后的算法了,但是后兩種算法卻是在此基礎(chǔ)上建立的。
- 時(shí)間與空間不可兼得。
分代收集算法(新生代的GC+老年代的GC)
當(dāng)前商業(yè)虛擬機(jī)都采用分代收集算法。
分代的垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的。因此,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率。
在新生代,每次垃圾收集器都發(fā)現(xiàn)有大批對(duì)象死去,只有少量的存活,那就選擇復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。 在老年代因?yàn)閷?duì)象的存活率較高、沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清除”或者“標(biāo)記-整理”算法進(jìn)行回收。
注:老年代的對(duì)象中,有一小部分是因?yàn)樵谛律厥諘r(shí),老年代做擔(dān)保,進(jìn)來(lái)的對(duì)象;絕大部分對(duì)象是因?yàn)楹芏啻蜧C都沒(méi)有被回收掉而進(jìn)入老年代。
總結(jié)
以上是生活随笔為你收集整理的JVM-05垃圾收集Garbage Collection(中)【垃圾收集算法】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JVM-04垃圾收集Garbage Co
- 下一篇: JVM-07垃圾收集Garbage Co