垃圾回收基礎(chǔ)

垃圾回收(Garbage Collection,GC)是虛擬機(jī)內(nèi)存管理很重要的一個模塊,它幫助虛擬機(jī)在技術(shù)層面上做到了內(nèi)存動態(tài)分配后的回收。垃圾回收主要發(fā)生在線程公有的區(qū)域。GC主要完成這樣三件事情:

  • 確定哪些內(nèi)存需要回收;

  • 確定待回收的內(nèi)存在何時回收;

  • 如何來做垃圾回收。

接下來就以如何做這三件事情為切入點,詳細(xì)介紹一下GC的相關(guān)基礎(chǔ)。

注:為什么垃圾回收發(fā)生在線程公有區(qū)域?
私有區(qū)域會隨著線程的消亡而消亡,但是共有區(qū)域不會,公有區(qū)域內(nèi)存的回收需要GC來完成。了解Java內(nèi)存模型的小伙伴都知道,堆和方法區(qū)是公有的,所以GC目標(biāo)區(qū)域在堆和方法區(qū)。

如何確定對象已死?

堆中幾乎存放著Java程序中所有的對象實例,對于回收堆上的內(nèi)存來說,確定哪些內(nèi)存需要回收實質(zhì)就是確定哪些對象已經(jīng)死了,而后續(xù)的垃圾回收也就是將這些已死對象的內(nèi)存做回收。那么,如何確定對象已死?

引用計數(shù)算法

確定一個對象是否已經(jīng)死了就是看這個對象是不是沒有引用了。在很多資料上都看到了判斷對象是不是有引用的算法是這樣子的:給對象添加一個引用計數(shù)器,一旦有地方引用它時,計數(shù)器值+1,引用失效時,計數(shù)器值-1,當(dāng)引用計數(shù)器值為0時,該對象不再被引用??陀^的說,引用計數(shù)算法實現(xiàn)很簡單,判斷效率也很高,但是,假如存在對象之間互相引用,這個算法就解決不了了,所以Java的GC并沒有選用該算法來管理內(nèi)存。

舉個簡單的例子:對象A和B都有私有屬性instance,賦值令A(yù).instance = B,B.instance = A,A和B互相引用。


demo

GC日志:


GC日志

從GC日志可以看出,虛擬機(jī)并沒有因為a和b互相引用就不回收它們,這也說明虛擬機(jī)并不是通過引用計數(shù)算法來判斷對象是否存在引用的。

根搜索算法

以GC Roots對象作為起始點,從這些節(jié)點依次向下搜索,如果當(dāng)前對象到GC Roots沒有任何的路徑相連(對象不可達(dá))時,那么,當(dāng)前對象沒有引用。

引用鏈:對象到GC Roots的走過的路徑

在Java中,以下對象可作為GC Roots:

  1. Java虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象;

  2. 本地方法棧中引用的對象;

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

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

根搜索算法中不可達(dá)的對象,是不是就非死不可呢?其實并不是的,要真正宣告一個對象死亡,至少要經(jīng)過兩次標(biāo)記。在這里必須要多嘴幾句說說finalize。

finalize

有過面試經(jīng)歷的人,可能都被問到了final,finally和finalize的區(qū)別。前兩個關(guān)鍵字比較常用,在這里就不再做介紹了。finalize是一個方法名,Java允許使用finalize方法在垃圾收集器將對象從內(nèi)存中清除出去之前做必要的清理工作,所以很多文章中又稱它為析構(gòu)函數(shù)的替代者。那么finalize方法是何時何地被調(diào)用呢?每次GC都會調(diào)用它么?

  • finalize方法調(diào)用過程
    待回收的對象被標(biāo)記出來之后,會對這些對象做一次篩選,看對象是否有必要執(zhí)行finalize方法。判斷對象是否有必要執(zhí)行該方法主要有以下兩個依據(jù):

    • 對象有沒有覆蓋finalize方法;

    • 對象已覆蓋finalize方法,檢查finalize方法是否被虛擬機(jī)調(diào)用過,如果已被調(diào)用,就不需要再次執(zhí)行。

    驗證待回收對象有必要執(zhí)行finalize方法后,這個對象將會被放置到隊列F-Queue中,并且會被虛擬機(jī)的單獨線程Finalizer執(zhí)行。一定需要注意的是,這里的執(zhí)行不代表執(zhí)行成功,只觸發(fā)finalize方法,不承諾等待該方法執(zhí)行結(jié)束。為什么這么設(shè)計呢?

    • 對象finalize方法執(zhí)行時間較高,會導(dǎo)致F-Queue中的其他對象等待時間較長;

    • 如果程序員不小心,在finalize方法的實現(xiàn)中寫了個死循環(huán),這時候F-Queue中的其他對象會一直處于等待狀態(tài),甚至導(dǎo)致整個GC崩潰。

注:看到這里大致已經(jīng)了解了finalize的執(zhí)行過程以及執(zhí)行次數(shù),千萬別認(rèn)為finalize方法會一直都被執(zhí)行,其實它只會被執(zhí)行一次,一旦它在上次GC已經(jīng)被執(zhí)行,以后就不會再執(zhí)行,除非你重啟系統(tǒng)。

  • finalize使用案例
    finalize使用案例

    運(yùn)行結(jié)果:

    案例通過finalize方法成功的實現(xiàn)了逃脫被GC,但是從運(yùn)行結(jié)果可以看出,第一次確實成功的逃脫了,但是第二次就沒那么好運(yùn)了,被GC掉了。這也能說明finalize方法只能被執(zhí)行一次。

如何回收內(nèi)存?

在確定需要回收的對象之后,接下來就將這些內(nèi)存做回收。先來看看垃圾回收算法吧,本文只簡要介紹垃圾回收算法思想,至于實現(xiàn)后面文章會給出較詳細(xì)的分析。

  • 標(biāo)記 - 清除算法
    標(biāo)記 - 清除算法是最基礎(chǔ)的垃圾收集算法。它分為兩個階段:

    • 標(biāo)記階段:標(biāo)記出所有待回收的對象;

    • 清除階段:清除掉所有被標(biāo)記的對象;

    該算法實現(xiàn)過程簡單,成本低,但是它有這樣幾個缺點:

    • 效率較低;

    • 標(biāo)記清除并沒有對內(nèi)存做過壓縮整理,這樣會導(dǎo)致清除后出現(xiàn)大量的內(nèi)存碎片,空間內(nèi)存碎片較多可能會導(dǎo)致在后續(xù)程序需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存空間而不得不提前觸發(fā)一次GC。

    為什么說它是最基礎(chǔ)的垃圾收集算法,是因為后續(xù)的垃圾收集算法都是基于該算法思路并對其不足之處改進(jìn)得到的。

  • 復(fù)制算法
    為了改善標(biāo)記 - 清除算法的效率問題,復(fù)制算法出現(xiàn)了。它將可用內(nèi)存劃分為大小固定的兩塊,每次只使用其中的一塊,當(dāng)這一塊內(nèi)存使用完了,就將還活著的對象復(fù)制到另外一塊內(nèi)存上,一次性清理掉已使用過的內(nèi)存空間。這樣子做的好處就是每次的垃圾收集只對其中一塊內(nèi)存進(jìn)行回收,效率提升,而且,復(fù)制算法在回收內(nèi)存不會產(chǎn)生垃圾碎片。但是,該算法有個不好處就是它將原來的內(nèi)存縮小了。一般young區(qū)采用該算法來回收內(nèi)存。

注:HotSpot默認(rèn)Eden區(qū)和Survivor區(qū)大小比例為8:1,至于為什么設(shè)置這個比例是因為young區(qū)的對象98%都是朝生夕死的。當(dāng)然,JVM也提供參數(shù)以供使用者來更改Eden區(qū)和Survivor區(qū)的大小比例。

  • 標(biāo)記 - 整理算法
    由于每次要執(zhí)行較多的復(fù)制操作,復(fù)制收集算法在對象存活率比較高的區(qū)域做內(nèi)存回收就比較費勁了。更重要的一個點就是如果不想浪費一部分內(nèi)存空間,就需要額外的空來進(jìn)行分配擔(dān)保,來應(yīng)對被使用的內(nèi)存中所有對象都存活的情況。根據(jù)old區(qū)的特點,標(biāo)記 - 整理算法被提出。該算法的標(biāo)記的過程與標(biāo)記 - 清除算法一致,但是,后續(xù)的過程就有區(qū)別了,標(biāo)記 - 整理算法不會直接對已標(biāo)記的對象做內(nèi)存回收,而是會讓所有還存活的對象移動到一端,然后清除掉這一端邊界以外的內(nèi)存。

垃圾回收算法的思想到這里就分析完畢了,要想完整的了解垃圾回收,還需要了解垃圾回收載體 - 垃圾收集器??紤]到垃圾收集器類型較多,分析篇幅較大,就不再本文做相關(guān)分析了,會在后續(xù)的文章就垃圾收集器做詳細(xì)的分析。

到這里,垃圾回收的相關(guān)基礎(chǔ)內(nèi)容就介紹完畢了,歡迎大家繼續(xù)關(guān)注后續(xù)的垃圾回收更深層次的介紹。

最后編輯于
?著作權(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)容

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