一巫财、垃圾收集器判斷對(duì)象已死的算法
1.對(duì)象已死嗎?
判斷方法有以下兩種:
-
1.引用計(jì)數(shù)法
給對(duì)象添加一個(gè)引用計(jì)數(shù)器缺亮,每當(dāng)有一個(gè)地方引用它時(shí)翁涤,計(jì)數(shù)器就加1;當(dāng)引用失效時(shí)萌踱,計(jì)數(shù)器數(shù)值就減1葵礼;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。
但是這種方法有個(gè)弊端:很難解決對(duì)象之間相互循環(huán)引用的問(wèn)題并鸵。 -
2.可達(dá)性分析算法
當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連(用圖論的話來(lái)說(shuō)就是從 GC Roots 到這個(gè)對(duì)象不可達(dá)時(shí))鸳粉,證明這個(gè)對(duì)象是不可用的。
-
3.四種引用類型
強(qiáng)引用:
Object obj = new Object()
园担,這就是強(qiáng)引用届谈。只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象弯汰。軟引用:軟引用用來(lái)描述一些還有用但并非必需的對(duì)象艰山。對(duì)于軟引用關(guān)聯(lián)的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前咏闪,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收曙搬。如果這次回收還沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常鸽嫂。使用
SoftRefrence類
實(shí)現(xiàn)軟引用纵装。弱引用:弱引用也是用來(lái)描述非必須對(duì)象的,但是強(qiáng)度比軟引用更弱据某。被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾回收之前橡娄。垃圾收集器工作時(shí),無(wú)論當(dāng)前內(nèi)存是否足夠癣籽,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象挽唉。使用
WeakRefrence類
實(shí)現(xiàn)弱引用。虛引用:這是最弱的一種引用關(guān)系筷狼。對(duì)象的生存時(shí)間與虛引用完全沒(méi)有關(guān)系橱夭,無(wú)法通過(guò)虛引用獲得對(duì)象實(shí)例。虛引用的唯一作用就是在對(duì)象被垃圾回收時(shí)收到一個(gè)系統(tǒng)通知桑逝。使用
PhantomRefrence類
實(shí)現(xiàn)虛引用。
二俏让、垃圾收集算法
-
1.標(biāo)記-清除算法(
Mark-Sweep
)最基礎(chǔ)的收集算法是“標(biāo)記-清除算法”楞遏。這個(gè)算法分為兩個(gè)階段:標(biāo)記和清除茬暇。
標(biāo)記:在標(biāo)記階段,collector
從mutator根
對(duì)象開(kāi)始進(jìn)行遍歷寡喝,對(duì)從mutator根
對(duì)象可以訪問(wèn)到的對(duì)象都打上一個(gè)標(biāo)識(shí)糙俗,一般是在對(duì)象的header
中,將其記錄為可達(dá)對(duì)象预鬓。
清除:在清除階段巧骚,collector
對(duì)堆內(nèi)存(heap memory)
從頭到尾進(jìn)行線性的遍歷,通過(guò)讀取對(duì)象的 header
信息,如果發(fā)現(xiàn)某個(gè)對(duì)象沒(méi)有標(biāo)記為可達(dá)對(duì)象,則就將其回收格二。標(biāo)記為可達(dá)對(duì)象的對(duì)象劈彪,將重新去掉 header
信息的標(biāo)志位,以準(zhǔn)備下次垃圾回收顶猜。
但是這種方法有2個(gè)不足:
1.效率問(wèn)題沧奴,標(biāo)記和清除兩個(gè)過(guò)程效率都不高。
2.空間問(wèn)題长窄,標(biāo)記清除之后會(huì)產(chǎn)生大量的不連續(xù)的內(nèi)存碎片滔吠,空 間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾回收動(dòng)作挠日。
-
2.復(fù)制算法 (Copying)
復(fù)制算法將內(nèi)存劃分為兩個(gè)區(qū)間疮绷,在任意時(shí)間點(diǎn),所有動(dòng)態(tài)分配的對(duì)象都只能分配在其中一個(gè)區(qū)間(稱為活動(dòng)區(qū)間)嚣潜,而另外一個(gè)區(qū)間(稱為空閑區(qū)間)則是空閑的冬骚。
當(dāng)有效內(nèi)存空間耗盡時(shí),JVM 將暫停程序運(yùn)行郑原,開(kāi)啟復(fù)制算法 GC 線程唉韭。接下來(lái) GC 線程會(huì)將活動(dòng)區(qū)間內(nèi)的存活對(duì)象,全部復(fù)制到空閑區(qū)間犯犁,且嚴(yán)格按照內(nèi)存地址依次排列属愤,與此同時(shí),GC線程將更新存活對(duì)象的內(nèi)存引用地址指向新的內(nèi)存地址酸役。
而此時(shí)住诸,其實(shí)空閑區(qū)間和活動(dòng)區(qū)間已經(jīng)進(jìn)行了交換,垃圾對(duì)象已經(jīng)全部留在了原來(lái)的活動(dòng)區(qū)間涣澡,也就是現(xiàn)在的空閑區(qū)間贱呐。事實(shí)上,在活動(dòng)區(qū)間轉(zhuǎn)換為空間區(qū)間的同時(shí)入桂,垃圾對(duì)象已經(jīng)被一次性全部回收奄薇。
優(yōu)點(diǎn):每次內(nèi)存回收都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)不用考慮內(nèi)存碎片的問(wèn)題抗愁,只要移動(dòng)堆頂指針馁蒂,按照順序分配內(nèi)存即可呵晚,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效沫屡。
缺點(diǎn):1.代價(jià)是將內(nèi)存縮小為原來(lái)的一半饵隙;2.如果對(duì)象的存活率很高,我們可以極端一點(diǎn)沮脖,假設(shè)是100%存活金矛,那么我們需要將所有對(duì)象都復(fù)制一遍,并將所有引用地址重置一遍勺届。復(fù)制這一工作所花費(fèi)的時(shí)間驶俊,在對(duì)象存活率達(dá)到一定程度時(shí),將會(huì)變的不可忽視涮因。
復(fù)制算法比較適合的場(chǎng)景:對(duì)象的存活率要非常低才行废睦,而且最重要的是,我們必須要克服50%內(nèi)存的浪費(fèi)养泡。
下面看看復(fù)制算法回收前和回收后的內(nèi)存模型:
回收前:
回收后:
由于 1嗜湃,4 對(duì)象不可達(dá),被回收掉澜掩,剩下的對(duì)象按照順序分配內(nèi)存购披。這時(shí)右邊的區(qū)域變成了活動(dòng)區(qū)間,左邊變成空閑區(qū)間肩榕,下次垃圾回收將再次調(diào)換回來(lái)刚陡。
-
3.標(biāo)記-整理算法 (Mark-Compact)
標(biāo)記-整理算法與標(biāo)記-清除算法非常相似,它也是分為兩個(gè)階段:標(biāo)記和整理株汉。
標(biāo)記:它的第一個(gè)階段與標(biāo)記-清除算法是一模一樣的筐乳,均是遍歷 GC Roots,然后將存活的對(duì)象標(biāo)記乔妈。
整理:移動(dòng)所有存活的對(duì)象蝙云,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收路召。因此勃刨,第二階段才稱為整理階段。
標(biāo)記-整理算法 GC 前后的圖示與復(fù)制算法的圖非常相似股淡,只是沒(méi)有了活動(dòng)區(qū)間和空閑區(qū)間的區(qū)別身隐,而過(guò)程又與標(biāo)記-清除算法非常相似。
回收前
標(biāo)記
標(biāo)記為 1 表示對(duì)象仍然可達(dá)唯灵。
整理
可見(jiàn)贾铝,1,4 對(duì)象被回收了,剩下的存活對(duì)象的內(nèi)存按照順序排列垢揩,這點(diǎn)很像復(fù)制算法大脉,回收后不存在內(nèi)存碎片問(wèn)題,而且避免了內(nèi)存減半的高額代價(jià)水孩。但是處理過(guò)程又想標(biāo)記-清除算法英古,不僅要標(biāo)記所有存活對(duì)象茅主,還要整理所有存活對(duì)象的引用地址用踩。效率也不高怀愧,在效率上要低于復(fù)制算法骚勘。
參考資料