這篇文章將為大家詳細(xì)講解有關(guān)深入淺析JVM中的垃圾收集器,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
我們擁有十多年網(wǎng)頁(yè)設(shè)計(jì)和網(wǎng)站建設(shè)經(jīng)驗(yàn),從網(wǎng)站策劃到網(wǎng)站制作,我們的網(wǎng)頁(yè)設(shè)計(jì)師為您提供的解決方案。為企業(yè)提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、微信開發(fā)、微信小程序定制開發(fā)、成都手機(jī)網(wǎng)站制作、html5、等業(yè)務(wù)。無(wú)論您有什么樣的網(wǎng)站設(shè)計(jì)或者設(shè)計(jì)方案要求,我們都將富于創(chuàng)造性的提供專業(yè)設(shè)計(jì)服務(wù)并滿足您的需求。
說(shuō)起垃圾收集(Garbage Collection,GC),大部分人都把這項(xiàng)技術(shù)當(dāng)做Java語(yǔ)言的伴生產(chǎn)物。事實(shí)上,GC的歷史遠(yuǎn)比Java久遠(yuǎn),1960年誕生于MIT的Lisp是第一門真正使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語(yǔ)言。當(dāng)List還在胚胎時(shí)期時(shí),人們就在思考GC需要完成的3件事情:
一、哪些內(nèi)存需要回收?
從JVM區(qū)域結(jié)構(gòu)看,可將這些區(qū)域劃分為“靜態(tài)內(nèi)存”和“動(dòng)態(tài)內(nèi)存”兩類。程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法3個(gè)區(qū)域是“靜態(tài)”的,因?yàn)檫@幾個(gè)區(qū)域的內(nèi)存分配和回收都具備確定性,都隨著線程而生,隨著線程而滅。但Java堆和方法區(qū)不一樣,內(nèi)存分配都存在不確定性,只有在程序處于運(yùn)行期間才能知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存和回收都是動(dòng)態(tài)的,垃圾收集器所關(guān)注的是這部分內(nèi)存。
在堆里面存放著Java世界幾乎所有的對(duì)象實(shí)例,垃圾回收器在對(duì)堆進(jìn)行回收前,第一件事情就是就是要確定這些對(duì)象哪些還"存活"著,哪些已經(jīng)"死去"。那么又怎么確定對(duì)象已經(jīng)"死去"呢?
1.引用計(jì)數(shù)法:
分配對(duì)象時(shí)給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是沒(méi)有再被使用了??陀^地說(shuō),引用計(jì)數(shù)法(Reference Counting)的實(shí)現(xiàn)簡(jiǎn)單,判斷效率也很高,但是在主流的Java虛擬機(jī)里面沒(méi)有選用引用計(jì)數(shù)法來(lái)管理內(nèi)存,其中最主要的原因是它很難解決對(duì)象之間相互循環(huán)引用的問(wèn)題。例如:
public class ReferenceCountingGC { public Object instance = null; private byte[] bigsize = new byte[2*1024*1024]; public static void testGC(){ ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; System.gc(); } }
當(dāng)設(shè)置objA = null;objB = null后這兩個(gè)對(duì)象再無(wú)任何引用,實(shí)際上這兩個(gè)對(duì)象已經(jīng)不可能再被訪問(wèn),但是它們因?yàn)榛ハ嘁弥鴮?duì)方,導(dǎo)致它們的引用計(jì)數(shù)都不為0,于是引用計(jì)數(shù)算法無(wú)法通知GC收集器回收它們。如果這個(gè)對(duì)象特別大,則會(huì)造成嚴(yán)重的內(nèi)存泄露。
2.可達(dá)性分析算法:
可達(dá)性分析(Reachability Analysis)的基本思想是通過(guò)一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過(guò)的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí)(也就是GC Roots到這個(gè)對(duì)象不可達(dá)),則證明此對(duì)象是不可用的。如下圖所示:
對(duì)象Object5、Object6、Object7相互雖然有關(guān)聯(lián),但是它們到GC Roots是不可達(dá)的,所以它們將會(huì)被判定為是可回收的對(duì)象。在Java語(yǔ)言中,可作為GC Roots的對(duì)象包括下面幾種:
二、什么時(shí)候回收?
虛擬機(jī)為了分析GC Roots這項(xiàng)工作必須在一個(gè)能確保一致性的快照中進(jìn)行,這里的“一致性”的意思就是指在整個(gè)分析期間整個(gè)執(zhí)行系統(tǒng)看起來(lái)就像被凍結(jié)在某個(gè)時(shí)間點(diǎn)上——這叫安全點(diǎn)。當(dāng)然,程序執(zhí)行時(shí)并非在所有地方都能停頓下來(lái)開始GC,只有到達(dá)安全點(diǎn)時(shí)才能暫停。安全點(diǎn)選址也有規(guī)定的,選定基本上是以程序“是否具有讓程序長(zhǎng)時(shí)間執(zhí)行的特征”為標(biāo)準(zhǔn)進(jìn)行選定的。這里的長(zhǎng)時(shí)間執(zhí)行的最明顯特征是指令列復(fù)用,例如方法調(diào)用、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等。
虛擬機(jī)為了能讓所有線程都“跑”到安全點(diǎn)上停頓下來(lái),設(shè)計(jì)了兩個(gè)方案:搶先式中斷和主動(dòng)式中斷。其中搶先式中斷是虛擬機(jī)發(fā)生GC時(shí),首先把所有線程全部中斷,如果發(fā)生有線程中斷的地方不在安全點(diǎn)上,就恢復(fù)線程,讓它“跑”到安全點(diǎn)上。這種方式現(xiàn)在比較用了。而主動(dòng)式中斷是虛擬機(jī)需要GC時(shí)僅僅簡(jiǎn)單的設(shè)置一個(gè)標(biāo)志,各個(gè)線程執(zhí)行到安全點(diǎn)時(shí)主動(dòng)去輪詢這個(gè)標(biāo)志,發(fā)現(xiàn)中斷標(biāo)志為真時(shí)就自己中斷掛起。
三、如何回收?
3.1 垃圾收集算法:
(1)標(biāo)記-清除(Mark-Sweep)算法
這是最基礎(chǔ)的算法,就像它名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記處所有需要回收的對(duì)象(如哪些內(nèi)存需要回收所描述的對(duì)象),對(duì)標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象,如下圖所示:
缺點(diǎn):一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高;另一個(gè)是空間問(wèn)題,標(biāo)記清除后悔產(chǎn)生大量的不連續(xù)的內(nèi)存碎片,可能會(huì)導(dǎo)致后續(xù)無(wú)法分配大對(duì)象而導(dǎo)致再一次觸發(fā)垃圾收集動(dòng)作。
(2)復(fù)制算法
為了針對(duì)標(biāo)記-清除算法的不足,復(fù)制算法將可用內(nèi)存容量劃分為大小相等的兩塊,每次只使用一塊。當(dāng)一塊的內(nèi)存用完了,就將還存活的對(duì)象復(fù)制到另一塊上面去。然后把已使用過(guò)的內(nèi)存空間一次清理掉,如下圖所示:
缺點(diǎn):使用內(nèi)存比原來(lái)縮小了一半。
現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來(lái)回收新生代,有企業(yè)分析的得出其實(shí)并不需求將內(nèi)存按1:1的比例劃分,因?yàn)樾律械膶?duì)象大部分都是“朝生夕死”的。所以,HotSpot虛擬機(jī)默認(rèn)的Eden和Survivor的大小比例是8:1。一塊Eden和兩塊Survivor,每次使用一塊Eden和一塊Survivor,也就是說(shuō)只有10%是浪費(fèi)的。如果另一塊Survivor都無(wú)法存放上次垃圾回收的對(duì)象時(shí),那這些對(duì)象將通過(guò)“擔(dān)保機(jī)制”進(jìn)入老年代了。
(3)標(biāo)記-整理(Mark-Compact)算法
復(fù)制算法一般是對(duì)對(duì)象存活率較低的一種回收操作,但對(duì)于對(duì)象存活率較高的內(nèi)存區(qū)域(老年代)來(lái)說(shuō),效果就不是那么理想了,標(biāo)記-整理算法因此誕生了。標(biāo)記-整理算法和標(biāo)記-清除算法差不多,都是一開始對(duì)回收對(duì)象進(jìn)行標(biāo)記,但后續(xù)不是直接對(duì)對(duì)象清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存,如下圖所示:
(4)分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收,而新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量的對(duì)象需要被回收,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。
3.2 垃圾收集器:
(1)七種垃圾收集器:
說(shuō)明:
注意:并行與并發(fā)
(2)常用五種組合:
(2.1)Serial/Serial Old:
特點(diǎn):
說(shuō)明:
STW(stop the world):編譯代碼時(shí)為每一個(gè)方法注入safepoint(方法中循環(huán)結(jié)束的點(diǎn)、方法執(zhí)行結(jié)束的點(diǎn)),在暫停應(yīng)用時(shí),需要等待所有的用戶線程進(jìn)入safepoint,之后暫停所有線程,然后進(jìn)行垃圾回收。
適用場(chǎng)合:
(2.2)ParNew/Serial Old:
說(shuō)明:
ParNew除了采用多GC線程來(lái)實(shí)現(xiàn)復(fù)制算法以外,其他都與Serial一樣,但是此組合中的Serial Old又是一個(gè)單GC線程,所以該組合是一個(gè)比較尷尬的組合,在單CPU情況下沒(méi)有Serial/Serial Old速度快(因?yàn)镻arNew多線程需要切換),在多CPU情況下又沒(méi)有之后的三種組合快(因?yàn)镾erial Old是單GC線程),所以使用其實(shí)不多。
-XX:ParallelGCThreads:指定ParNew GC線程的數(shù)量,默認(rèn)與CPU核數(shù)相同,該參數(shù)在于CMS GC組合時(shí),也可能會(huì)用到
(2.3)Parallel Scavenge/Parallel Old:
特點(diǎn):
說(shuō)明:
參數(shù)設(shè)置:
適用場(chǎng)合:
(2.4)ParNew/CMS:
說(shuō)明:
特點(diǎn):
1.年輕代ParNew收集器采用多個(gè)GC線程實(shí)現(xiàn)"復(fù)制"算法(包括掃描、復(fù)制)
2.年老代CMS收集器采用多線程實(shí)現(xiàn)"標(biāo)記-清除"算法
3.初始標(biāo)記與重新標(biāo)記都會(huì)暫停所有用戶線程(即STW),但是時(shí)間較短;并發(fā)標(biāo)記與并發(fā)清理時(shí)間較長(zhǎng),但是不需要STW
關(guān)于并發(fā)標(biāo)記期間怎樣記錄發(fā)生變動(dòng)的引用關(guān)系對(duì)象,在重新標(biāo)記期間怎樣掃描這些對(duì)象
缺點(diǎn):
參數(shù)設(shè)置:
適用場(chǎng)合:
用于處理很多的交互任務(wù)的情況
方法區(qū)的回收一般使用CMS,配置兩個(gè)參數(shù):-XX:+CMSPermGenSweepingEnabled與-XX:+CMSClassUnloadingEnabled
適用于一些需要長(zhǎng)期運(yùn)行且對(duì)相應(yīng)時(shí)間有一定要求的后臺(tái)程序
(2.5)G1
說(shuō)明:
原理:
運(yùn)作流程:
優(yōu)點(diǎn):
適用范圍:
關(guān)于深入淺析JVM中的垃圾收集器就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。