JVM的判斷對象是否已死和四種垃圾回收算法總結(jié)

面試題一:判斷對象是否已死

判斷對象是否已死就是找出哪些對象是已經(jīng)死掉的,以后不會再用到的,就像地上有廢紙、飲料瓶和百元大鈔,掃地前要先判斷出地上廢紙和飲料瓶是垃圾,百元大鈔不是垃圾。判斷對象是否已死有引用計數(shù)算法和可達(dá)性分析算法。

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

給每一個對象添加一個引用計數(shù)器,每當(dāng)有一個地方引用它時,計數(shù)器值加 1;每當(dāng)有一個地方不再引用它時,計數(shù)器值減 1,這樣只要計數(shù)器的值不為 0,就說明還有地方引用它,它就不是無用的對象。如下圖,對象 2 有 1 個引用,它的引用計數(shù)器值為 1,對象 1有兩個地方引用,它的引用計數(shù)器值為 2 。


image.png

這種方法看起來非常簡單,但目前許多主流的虛擬機(jī)都沒有選用這種算法來管理內(nèi)存,原因就是當(dāng)某些對象之間互相引用時,無法判斷出這些對象是否已死,如下圖,對象 1 和對象 2 都沒有被堆外的變量引用,而是被對方互相引用,這時他們雖然沒有用處了,但是引用計數(shù)器的值仍然是 1,無法判斷他們是死對象,垃圾回收器也就無法回收。

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

了解可達(dá)性分析算法之前先了解一個概念——GC Roots,垃圾收集的起點,可以作為 GC Roots 的有虛擬機(jī)棧中本地變量表中引用的對象、方法區(qū)中靜態(tài)屬性引用的對象、方法區(qū)中常量引用的對象、本地方法棧中 JNI(Native 方法)引用的對象。
當(dāng)一個對象到 GC Roots 沒有任何引用鏈相連(GC Roots 到這個對象不可達(dá))時,就說明此對象是不可用的,是死對象。
如下圖:object1、object2、object3、object4 和 GC Roots 之間有可達(dá)路徑,這些對象不會被回收,但 object5、object6、object7 到 GC Roots 之間沒有可達(dá)路徑,這些對象就被判了死刑。


image.png

上面被判了死刑的對象(object5、object6、object7)并不是必死無疑,還有挽救的余地。進(jìn)行可達(dá)性分析后對象和 GC Roots 之間沒有引用鏈相連時,對象將會被進(jìn)行一次標(biāo)記,接著會判斷如果對象沒有覆蓋 Object的finalize() 方法或者 finalize() 方法已經(jīng)被虛擬機(jī)調(diào)用過,那么它們就會被行刑(清除);如果對象覆蓋了 finalize() 方法且還沒有被調(diào)用,則會執(zhí)行 finalize() 方法中的內(nèi)容,所以在 finalize() 方法中如果重新與 GC Roots 引用鏈上的對象關(guān)聯(lián)就可以拯救自己,但是一般不建議這么做,周志明老師也建議大家完全可以忘掉這個方法~

3.方法區(qū)回收

上面說的都是對堆內(nèi)存中對象的判斷,方法區(qū)中主要回收的是廢棄的常量和無用的類。
判斷常量是否廢棄可以判斷是否有地方引用這個常量,如果沒有引用則為廢棄的常量。
判斷類是否廢棄需要同時滿足如下條件:
該類所有的實例已經(jīng)被回收(堆中不存在任何該類的實例)。
加載該類的 ClassLoader 已經(jīng)被回收。
該類對應(yīng)的 java.lang.Class 對象在任何地方?jīng)]有被引用(無法通過反射訪問該類的方法)。
面試題二:常用四種垃圾回收算法
常用的垃圾回收算法有四種:標(biāo)記-清除算法、復(fù)制算法、標(biāo)記-整理算法、分代收集算法。

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

分為標(biāo)記和清除兩個階段,首先標(biāo)記出所有需要回收的對象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象,如下圖。
缺點:標(biāo)記和清除兩個過程效率都不高;標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片。

image.png

2.復(fù)制算法

把內(nèi)存分為大小相等的兩塊,每次存儲只用其中一塊,當(dāng)這一塊用完了,就把存活的對象全部復(fù)制到另一塊上,同時把使用過的這塊內(nèi)存空間全部清理掉,往復(fù)循環(huán),如下圖。

缺點:實際可使用的內(nèi)存空間縮小為原來的一半,比較適合。


image.png

3.標(biāo)記-整理算法

先對可用的對象進(jìn)行標(biāo)記,然后所有被標(biāo)記的對象向一段移動,最后清除可用對象邊界以外的內(nèi)存,如下圖。


image.png

4.分代收集算法

把堆內(nèi)存分為新生代和老年代,新生代又分為 Eden 區(qū)、From Survivor 和 To Survivor。一般新生代中的對象基本上都是朝生夕滅的,每次只有少量對象存活,因此采用復(fù)制算法,只需要復(fù)制那些少量存活的對象就可以完成垃圾收集;老年代中的對象存活率較高,就采用標(biāo)記-清除和標(biāo)記-整理算法來進(jìn)行回收。


image.png

在這些區(qū)域的垃圾回收大概有如下幾種情況:

大多數(shù)情況下,新的對象都分配在Eden區(qū),當(dāng) Eden 區(qū)沒有空間進(jìn)行分配時,將進(jìn)行一次 Minor GC,清理 Eden 區(qū)中的無用對象。清理后,Eden 和 From Survivor 中的存活對象如果小于To Survivor 的可用空間則進(jìn)入To Survivor,否則直接進(jìn)入老年代);Eden 和 From Survivor 中還存活且能夠進(jìn)入 To Survivor 的對象年齡增加 1 歲(虛擬機(jī)為每個對象定義了一個年齡計數(shù)器,每執(zhí)行一次 Minor GC 年齡加 1),當(dāng)存活對象的年齡到達(dá)一定程度(默認(rèn) 15 歲)后進(jìn)入老年代,可以通過 -XX:MaxTenuringThreshold 來設(shè)置年齡的值。

當(dāng)進(jìn)行了 Minor GC 后,Eden 還不足以為新對象分配空間(那這個新對象肯定很大),新對象直接進(jìn)入老年代。
占 To Survivor 空間一半以上且年齡相等的對象,大于等于該年齡的對象直接進(jìn)入老年代,比如 Survivor 空間是 10M,有幾個年齡為 4 的對象占用總空間已經(jīng)超過 5M,則年齡大于等于 4 的對象都直接進(jìn)入老年代,不需要等到 MaxTenuringThreshold 指定的歲數(shù)。

在進(jìn)行 Minor GC 之前,會判斷老年代最大連續(xù)可用空間是否大于新生代所有對象總空間,如果大于,說明 Minor GC 是安全的,否則會判斷是否允許擔(dān)保失敗,如果允許,判斷老年代最大連續(xù)可用空間是否大于歷次晉升到老年代的對象的平均大小,如果大于,則執(zhí)行 Minor GC,否則執(zhí)行 Full GC。
當(dāng)在 java 代碼里直接調(diào)用 System.gc() 時,會建議 JVM 進(jìn)行 Full GC,但一般情況下都會觸發(fā) Full GC,一般不建議使用,盡量讓虛擬機(jī)自己管理 GC 的策略。

永久代(方法區(qū))中用于存放類信息,jdk1.6 及之前的版本永久代中還存儲常量、靜態(tài)變量等,當(dāng)永久代的空間不足時,也會觸發(fā) Full GC,如果經(jīng)過 Full GC 還無法滿足永久代存放新數(shù)據(jù)的需求,就會拋出永久代的內(nèi)存溢出異常。
大對象(需要大量連續(xù)內(nèi)存的對象)例如很長的數(shù)組,會直接進(jìn)入老年代,如果老年代沒有足夠的連續(xù)大空間來存放,則會進(jìn)行 Full GC。

搜索公眾號【Java耕耘者】點擊小助理,即可獲取大量優(yōu)質(zhì)電子書和一份Java高級架構(gòu)資料、Spring源碼分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式等視頻資料

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

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

  • 面試題一:判斷對象是否已死 判斷對象是否已死就是找出哪些對象是已經(jīng)死掉的,以后不會再用到的,就像地上有廢紙、飲料瓶...
    風(fēng)平浪靜如碼閱讀 449評論 0 1
  • JVM架構(gòu) 當(dāng)一個程序啟動之前,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū)),執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,845評論 0 7
  • 作者:一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-12】 更新日志 日期更新內(nèi)容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,330評論 0 7
  • Java和C++之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”,墻外面的人想進(jìn)來,墻里面的人想出來。 對象...
    胡二囧閱讀 1,330評論 0 4
  • 提到90后,第一反映是什么?叛逆?囂張?自我?有沒有人像我一樣,第一反映是老去? 作為一個93年出生的名副...
    蔣小八閱讀 237評論 0 1

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