您好,登錄后才能下訂單哦!
怎么判斷對(duì)象是否可以被回收?
共有2種方法,引用計(jì)數(shù)法和可達(dá)性分析
1.引用計(jì)數(shù)法
所謂引用計(jì)數(shù)法就是給每一個(gè)對(duì)象設(shè)置一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用這個(gè)對(duì)象時(shí),就將計(jì)數(shù)器加一,引用失效時(shí),計(jì)數(shù)器就減一。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)器為零時(shí),說(shuō)明此對(duì)象沒(méi)有被引用,也就是“死對(duì)象”,將會(huì)被垃圾回收.
引用計(jì)數(shù)法有一個(gè)缺陷就是無(wú)法解決循環(huán)引用問(wèn)題,也就是說(shuō)當(dāng)對(duì)象A引用對(duì)象B,對(duì)象B又引用者對(duì)象A,那么此時(shí)A,B對(duì)象的引用計(jì)數(shù)器都不為零,也就造成無(wú)法完成垃圾回收,所以主流的虛擬機(jī)都沒(méi)有采用這種算法。
public classReferenceFindTest{ publicstaticvoidmain(String[] args){ MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); object1.object = object2; object2.object = object1; object1 = null; object2 = null; } }
2.可達(dá)性算法(引用鏈法)
該算法的思想是:從一個(gè)被稱為GC Roots的對(duì)象開(kāi)始向下搜索,如果一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí),則說(shuō)明此對(duì)象不可用。
在java中可以作為GC Roots的對(duì)象有以下幾種:
雖然這些算法可以判定一個(gè)對(duì)象是否能被回收,但是當(dāng)滿足上述條件時(shí),一個(gè)對(duì)象比不一定會(huì)被回收。當(dāng)一個(gè)對(duì)象不可達(dá)GC Root時(shí),這個(gè)對(duì)象并不會(huì)立馬被回收,而是出于一個(gè)死緩的階段,若要被真正的回收需要經(jīng)歷兩次標(biāo)記。
如果對(duì)象在可達(dá)性分析中沒(méi)有與GC Root的引用鏈,那么此時(shí)就會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選的條件是是否有必要執(zhí)行finalize()方法。當(dāng)對(duì)象沒(méi)有覆蓋finalize()方法或者已被虛擬機(jī)調(diào)用過(guò),那么就認(rèn)為是沒(méi)必要的。
如果該對(duì)象有必要執(zhí)行finalize()方法,那么這個(gè)對(duì)象將會(huì)放在一個(gè)稱為F-Queue的對(duì)隊(duì)列中,虛擬機(jī)會(huì)觸發(fā)一個(gè)Finalize()線程去執(zhí)行,此線程是低優(yōu)先級(jí)的,并且虛擬機(jī)不會(huì)承諾一直等待它運(yùn)行完,這是因?yàn)槿绻鹒inalize()執(zhí)行緩慢或者發(fā)生了死鎖,那么就會(huì)造成F-Queue隊(duì)列一直等待,造成了內(nèi)存回收系統(tǒng)的崩潰。GC對(duì)處于F-Queue中的對(duì)象進(jìn)行第二次被標(biāo)記,這時(shí),該對(duì)象將被移除”即將回收”集合,等待回收。
堆內(nèi)存分代策略以及意義
策略
Java虛擬機(jī)將堆內(nèi)存劃分為新生代、老年戰(zhàn)和永久代,永久代是HotSpaot 虛擬機(jī)特有的概念,它采用永久代的方式來(lái)實(shí)現(xiàn)方法區(qū),其他的虛擬機(jī)實(shí)現(xiàn)沒(méi)有這一概念,而且HotSpot也有取消永久代的趨勢(shì),在JDK 1.7中HotSpot已經(jīng)開(kāi)始了“去永久化”,把原本放在永久代的字符串常量池移出。永久代主要存放常量、類信息、靜態(tài)變量等數(shù)據(jù)(移植到方法區(qū)),與垃圾回收關(guān)系不大,新生代和老年代是垃圾回收的主要區(qū)域。
新生代(Young)
新生成的對(duì)象優(yōu)先存放在新生代中,新生代對(duì)象朝生夕死,存活率很低,在新生代中,常規(guī)應(yīng)用進(jìn)行一次垃圾收集-般可以回收70% ~ 95%的空間,回收效率很高。
老年代(OldGenerationn)
在新生代中經(jīng)歷了多次(具體看虛擬機(jī)配置的閥值)GC后仍然存活下來(lái)的對(duì)象會(huì)進(jìn)入老年代中。老年代中的對(duì)象生命周期較長(zhǎng),存活率比較高,在老年代中進(jìn)行GC的頻率相對(duì)而言較低,而且回收的速度也比較慢。
永久代(PermanentGenerationn)
永久代存儲(chǔ)類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),對(duì)這一區(qū)域而言,Java虛擬機(jī)規(guī)范指出可以不進(jìn)行垃圾收集,一般而言不會(huì)進(jìn)行垃圾回收。
意義
有了內(nèi)存分代,新創(chuàng)建的對(duì)象會(huì)在新生代中分配內(nèi)存,經(jīng)過(guò)多次回收仍然存活下來(lái)的對(duì)象存放在老年代中,靜態(tài)屬性、類信息等存放在永久代中,新生代中的對(duì)象存活時(shí)間短,只需要在新生代區(qū)域中頻繁進(jìn)行GC,老年代中對(duì)象生命周期長(zhǎng),內(nèi)存回收的頻率相對(duì)較低,不需要頻繁進(jìn)行回收,永久代中回收效果太差, 一般不進(jìn)行垃圾回收,還可以根據(jù)不同年代的特點(diǎn),采用不同的垃圾收集算法。分代垃圾收集大大提升了垃圾收集效率,這些都是JVM分代的好處。
垃圾回收算法
1.復(fù)制算法
復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊,當(dāng)一塊內(nèi)存用完時(shí),就將還存活的對(duì)象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存,再將第二塊上的對(duì)象復(fù)制到第一塊。但是這種方式,內(nèi)存的代價(jià)太高,每次基本上都要浪費(fèi)一半的內(nèi)存。
2.標(biāo)記清除算法
是JVM垃圾回收算法中最古老的一個(gè),該算法共分成兩個(gè)階段,第一階段從引用根節(jié)點(diǎn)開(kāi)始標(biāo)記所有被引用的對(duì)象,第二階段遍歷整個(gè)堆,清除未被標(biāo)記的對(duì)象。該算法的缺點(diǎn)是需要暫停整個(gè)應(yīng)用,并且在回收以后未使用的空間是不連續(xù),即內(nèi)存碎片,會(huì)影響到存儲(chǔ)。
3.標(biāo)記整理算法
此算法結(jié)合了標(biāo)記-清楚算法和復(fù)制算法的優(yōu)點(diǎn),也分為兩個(gè)階段,第一階段從引用根節(jié)點(diǎn)開(kāi)始標(biāo)記所有被引用的對(duì)象,第二階段遍歷整個(gè)堆,在回收不存活的對(duì)象占用的空間后,會(huì)將所有的存活對(duì)象往左端空閑空間移動(dòng),并更新對(duì)應(yīng)的指針。標(biāo)記-整理算法是在標(biāo)記-清除算法的基礎(chǔ)上,又進(jìn)行了對(duì)象的移動(dòng),因此成本更高,但是卻解決了內(nèi)存碎片的問(wèn)題,按順序排放,同時(shí)解決了復(fù)制算法所需內(nèi)存空間過(guò)大的問(wèn)題。
4.分代收集
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區(qū)之外還有一個(gè)代就是永久代(Permanet Generation)。老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收,而新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量的對(duì)象需要被回收,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。
a.年輕代回收算法(核心其實(shí)就是復(fù)制算法)
HotSpot將新生代劃分為三塊,-塊較大的Eden空間和兩塊較小的Survivor空間,默認(rèn)比例為8: 1: 1。劃分的目的是因?yàn)镠otSpot采用復(fù)制算法來(lái)回收新生代,設(shè)置這個(gè)比例是為了充分利用內(nèi)存空間,減少浪費(fèi)。新生成的對(duì)象在Eden區(qū)分配(大對(duì)象除外,大對(duì)象直接進(jìn)入老年代) ,當(dāng)Eden區(qū)沒(méi)有足夠的空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次Minor GC。GC開(kāi)始時(shí),對(duì)象只會(huì)存在于Eden區(qū)和From Survivor區(qū),To Survivor區(qū)是空的(作為保留區(qū)域)。
GC進(jìn)行時(shí),Eden區(qū)中所有存活的對(duì)象都會(huì)被復(fù)制到To Survivor區(qū),而在FromSurvivor區(qū)中,仍存活的對(duì)象會(huì)根據(jù)它們的年齡值決定去向,年齡值達(dá)到閥值(默認(rèn)為15 ,新生代中的對(duì)象每熬過(guò)一輪垃圾回收年齡值就加1 ,GC分代年齡存儲(chǔ)在對(duì)象的header中)的對(duì)象會(huì)被移到老年代中,沒(méi)有達(dá)到閥值的對(duì)象會(huì)被復(fù)制到To Survivor區(qū)。
接著清空Eden區(qū)和From Survivor區(qū),新生代中存活的對(duì)象都在To Survivor區(qū)。接著, From Survivor區(qū)和To Survivor區(qū)會(huì)交換它們的角色,也就是新的To Survivor區(qū)就是上次GC清空的FromSurvivor區(qū),新的From Survivor區(qū)就是.上次GC的To Survivor區(qū),總之,不管怎樣都會(huì)保證To Survivor區(qū)在一輪GC后是空的(其實(shí)這就是分代收集算法中的年輕代回收算法,稍后我們會(huì)看到)。
GC時(shí)當(dāng)To Survivor區(qū)沒(méi)有足夠的空間存放上一次新生代收集下來(lái)的存活對(duì)象時(shí),需要依賴?yán)夏甏M(jìn)行分配擔(dān)保,將這些對(duì)象存放在老年代中。
b.老年代回收算法(回收主要以標(biāo)記-整理為主)
1)在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到年老代中。因此,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象。
2)內(nèi)存比新生代也大很多(大概比例是1:2),當(dāng)老年代內(nèi)存滿時(shí)觸發(fā)Major GC即Full GC,F(xiàn)ull GC發(fā)生頻率比較低,老年代對(duì)象存活時(shí)間比較長(zhǎng),存活率標(biāo)記高。
c. 持久代(Permanent Generation)的回收算法
用于存放靜態(tài)文件,如Java類、方法等。持久代對(duì)垃圾回收沒(méi)有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Hibernate 等,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來(lái)存放這些運(yùn)行過(guò)程中新增的類。在該區(qū)內(nèi)很少發(fā)生垃圾回收,但是并不代表不發(fā)生GC,在這里進(jìn)行的GC主要是對(duì)持久代里的常量池和對(duì)類型的卸載。
條件:
1)該類所有的實(shí)例都已經(jīng)被回收,即Java堆中不存在該類的任何實(shí)例;
2)加載該類的ClassLoader已經(jīng)被回收;
3)該類對(duì)應(yīng)的java.lang.Class對(duì)象沒(méi)有在任何地方被引用,無(wú)法在任何地方通過(guò)反射訪問(wèn)該類的方法。
虛擬機(jī)可以對(duì)滿足上述3個(gè)條件的無(wú)用類進(jìn)行回收,此處僅僅是“可以”,而并不是和對(duì)象一樣,不使用了就必然回收!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。