JVM:垃圾收集器與內(nèi)存分配策略

上一章我們介紹了Java內(nèi)存區(qū)域的作用,其中程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧3個(gè)區(qū)域隨線程而生,隨線程而滅,棧中的棧幀隨著方法的進(jìn)入與退出有條不紊的執(zhí)行著出棧和入棧的操作。其中每一個(gè)棧幀中分配多少內(nèi)存已經(jīng)在類結(jié)構(gòu)確定下來(lái)時(shí)就已知了。所以這幾個(gè)區(qū)域內(nèi)就不需要過(guò)多考慮回收的問(wèn)題,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí),內(nèi)存自然就回收了。

所以垃圾收集器關(guān)注就是Java堆和方法區(qū)的內(nèi)存。

因?yàn)槲覀冎挥性诔绦蜻\(yùn)行期間才能知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的。

如何判斷對(duì)象是否需要收集?

堆中存放著幾乎所有的對(duì)象的實(shí)例。如何判斷是垃圾收集器的首要任務(wù)。

1.引用計(jì)數(shù)算法

這也是大家都了解的算法:給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它的時(shí)候,計(jì)數(shù)器值就+1,當(dāng)引用失效就-1,任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能在被使用的。

客觀的說(shuō)這十個(gè)不錯(cuò)的算法,實(shí)現(xiàn)簡(jiǎn)單而且判定效率高,但是至少主流的Java虛擬機(jī)里面沒(méi)有選用這個(gè)算法管理內(nèi)存,因?yàn)樗茈y解決對(duì)象之間相互循環(huán)引用的問(wèn)題。

運(yùn)行結(jié)果證明,虛擬機(jī)并沒(méi)有因?yàn)檫@兩個(gè)對(duì)象互相引用就不回收他們,這也從側(cè)面分析出Java虛擬機(jī)并沒(méi)有應(yīng)用引用計(jì)數(shù)算法。

2.可達(dá)性分析算法

這個(gè)算法的基本思想就是通過(guò)一系列的成為“GC Roots”的對(duì)象左右起始點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索所有過(guò)的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí),則證明這個(gè)對(duì)象時(shí)不可用的,被判定為可回收對(duì)象。

在Java中,可作為GC Roots的對(duì)象包括以下幾種

1.虛擬機(jī)棧中引用的對(duì)象。

2.方法區(qū)中類靜態(tài)屬性引用的對(duì)象。

3.方法區(qū)中常量引用的對(duì)象。

4.本地方法棧中本地方法引用的對(duì)象。

2.1 生存還是死亡

即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非是非死不可的。這時(shí)候只是進(jìn)入了“緩刑”階段,要宣告回收一個(gè)對(duì)象,至少要經(jīng)歷兩次標(biāo)記過(guò)程

1.對(duì)象進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與GC Roots相連接的引用鏈,它會(huì)被標(biāo)記并進(jìn)行一次篩選。

2.篩選的條件是此對(duì)象是否有必要執(zhí)行 finalize() 的方法(當(dāng)對(duì)象沒(méi)有覆蓋 finalize() 方法或者該方法已經(jīng)被虛擬機(jī)調(diào)用過(guò),則虛擬機(jī)認(rèn)為這兩種情況都是“沒(méi)有必要執(zhí)行”)

3.如果有必要執(zhí)行,該對(duì)象就會(huì)被放置在F-Queue隊(duì)列中,并在稍后由一個(gè)虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的Finalizer線程去執(zhí)行。

4.(finalize() 方法是對(duì)象逃脫的最后一次機(jī)會(huì)。)稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模標(biāo)記,如果對(duì)象要拯救自己---------只要重新與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)即可。在第二次標(biāo)記時(shí)就會(huì)把它移除“即將回收”的集合。

3.垃圾收集算法?

3.1 標(biāo)記-清除算法

最基礎(chǔ)的收集算法就是“標(biāo)記-清除算法”,分為兩個(gè)階段:首先標(biāo)記處所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象。

之所以說(shuō)它是最基礎(chǔ)的算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其不足進(jìn)行改進(jìn)而得到的。

它有兩個(gè)不足:一個(gè)是效率問(wèn)題,一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后過(guò)程中需要分配大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)領(lǐng)一次垃圾收集動(dòng)作。

3.2 復(fù)制算法

為了解決效率問(wèn)題,出現(xiàn)了“復(fù)制”算法,它可將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用一塊,當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另一塊上面,再把已經(jīng)使用過(guò)的內(nèi)存空間一次清理掉,這樣使得每次只對(duì)一半的區(qū)域進(jìn)行內(nèi)存回收。

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單、運(yùn)行高效,不需要考慮內(nèi)存碎片等復(fù)雜情況。

缺點(diǎn):代價(jià)是將內(nèi)存縮小為了原來(lái)的一半,代價(jià)有點(diǎn)高:(

現(xiàn)在的商業(yè)虛擬機(jī)都采用這種方法來(lái)回收新生代,將內(nèi)存分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor 。當(dāng)回收時(shí),將 Eden 和 Survivor 中還存活著的對(duì)象一次性地復(fù)制到另外一塊 Survivor 空間上,最后清理掉 Eden 和剛才用過(guò)的 Survivor 空間。(如果Survivor空間不夠用時(shí),需要依賴其他內(nèi)存(老年代)進(jìn)行分配擔(dān)保)。

3.3 標(biāo)記-整理算法?

根據(jù)老年代的特點(diǎn),提出了“標(biāo)記-整理”算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。

4.內(nèi)存分配與回收策略

對(duì)象的內(nèi)存分配,往大方向講,就是在堆上分配,對(duì)象主要分配在新生代的 Eden 區(qū)上。

4.1 對(duì)象優(yōu)先在 Eden 分配

大多數(shù)情況下,對(duì)象在新生代 Eden 區(qū)中分配、當(dāng) Eden 區(qū)沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次 Minor GC。

Minor GC(新生代GC):指發(fā)生在新生代的垃圾收集動(dòng)作,因?yàn)镴ava對(duì)象大多都具備朝生夕滅的特性,所以Minor GC非常頻繁,一般回収速度也比較快。

Major GC/Full GC(老年代GC):指發(fā)生在老年代的GC,出現(xiàn)了Major GC,經(jīng)常會(huì)伴隨至少一次的Major GC。Major GC的速度一般會(huì)比Minor GC慢10倍以上。

4.2 大對(duì)象直接進(jìn)入老年代

所謂的大對(duì)象是指,需要大量連續(xù)內(nèi)存空間的Java對(duì)象,最典型的大對(duì)象就是那種很長(zhǎng)的字符串以及數(shù)組。大對(duì)象對(duì)虛擬機(jī)分配來(lái)說(shuō)就是一個(gè)壞消息,經(jīng)常出現(xiàn)大對(duì)象會(huì)容易導(dǎo)致內(nèi)存還有不少空間時(shí)就提前觸發(fā)垃圾收集以獲取足夠的空間來(lái)“安置”它們。

4.3 長(zhǎng)期存活的對(duì)象將進(jìn)入老年代

既然虛擬機(jī)采用了分代收集的思想來(lái)管理內(nèi)存,那么內(nèi)存回收時(shí)就必須能識(shí)別哪些對(duì)象應(yīng)放在新生代,哪些放在老年代中。為了做到這一點(diǎn),虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡(Age)。如果對(duì)象在Eden曲出生并警告過(guò)第一次MinorGC后仍然存活,并且能夠被Survivor容納的話,將被移動(dòng)到Survivor空間中,并且對(duì)象年齡設(shè)為1。每熬過(guò)一次MinorGC,年齡就增加一歲,當(dāng)年齡到一定程度時(shí)(默認(rèn)為15),就會(huì)晉升到老年代中。



摘自《深入理解Java虛擬機(jī)》第二版

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容