我也來談一談Java GC

以前寫c/c++的時(shí)候雖然也有shared_ptr這樣的自動(dòng)內(nèi)存管理機(jī)制,但是它內(nèi)部其實(shí)是通過引用計(jì)數(shù)的原理進(jìn)行內(nèi)存管理的,容易產(chǎn)生循環(huán)應(yīng)用的問題,也沒有什么實(shí)際意義上的內(nèi)存收集器搁骑。和java的內(nèi)存收集機(jī)制差別很大,所以一直對java的內(nèi)存收集機(jī)制抱有很強(qiáng)的好奇心罗侯。

最近在看《深入理解 Java 虛擬機(jī)-Jvm 高級特性與最佳實(shí)踐》,里面對java垃圾收集講的挺不錯(cuò)的酌泰。然后再將書中沒有講透的知識在網(wǎng)上搜索了下,整理成了這篇博客,哪天一時(shí)半會(huì)記不起來的時(shí)候還能回來瞧一瞧脾歧。

GC Roots

在Java虛擬機(jī)中,存在自動(dòng)內(nèi)存管理和垃圾回收機(jī)制,能自動(dòng)回收沒有用的對象的內(nèi)存。

那怎么去判定一個(gè)對象是否還有用呢?java中是通過可達(dá)性分析來判定的晒骇。

具體的做法就是從一系列被稱作"GC Roots"的對象作為起始點(diǎn),從這些對象開始通過引用關(guān)系進(jìn)行搜索。當(dāng)GC Roots到某個(gè)對象沒有任何引用鏈相連,則證明此對象是不可用的,是不需要存活,可以被清理的。

例如下圖的object1洪囤、object2徒坡、object3、object4就是從GC Roots可達(dá)的,不能被回收箍鼓。而object5崭参、object6、object7雖然相互引用,但從GC Roots不可達(dá),證明程序中無法訪問到它們,所以它們是無用的,可以被回收款咖。

1.png

在Java中,可以作為GC Roots的對象包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法去中常量引用的對象
  • 本地方法棧中JNI引用的對象

Java 堆中的內(nèi)存分配與回收

對于大多數(shù)應(yīng)用來說,Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊何暮。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存铐殃。這一點(diǎn)在Java虛擬機(jī)規(guī)范中的描述是:所有的對象實(shí)例以及數(shù)組都要在堆上分配海洼。

由于現(xiàn)在收集器基本都是采用的分代收集算法,所以Java堆中還可以細(xì)分為新生代和老年代。

新生代中的Minor GC

大部分對象被創(chuàng)建時(shí),內(nèi)存的分配首先發(fā)生在年輕代(占用內(nèi)存比較大的對象如數(shù)組,會(huì)被直接分配到老年帶)富腊。大部分的對象在創(chuàng)建之后很快就不再使用了,因此很快變成不可達(dá)的狀態(tài),于是被新生代的GC機(jī)制清理掉坏逢。這個(gè)GC機(jī)制被稱為Minor GC或叫Young GC。

新生代可以分為3個(gè)區(qū)域:一個(gè)Eden區(qū)和兩個(gè)Survivor區(qū)赘被。兩個(gè)Survivor中總有一個(gè)是空的,我們叫他Survivor To區(qū),還有一個(gè)非空的,我們叫他Survivor From區(qū)是整。Eden區(qū)和兩個(gè)Survivor區(qū)的大小為8:1:1。

對象被創(chuàng)建的時(shí)候,絕大部分都是被分配在Eden區(qū)民假。Eden區(qū)是連續(xù)的內(nèi)存空間浮入,因此在其上分配內(nèi)存極快。當(dāng)Eden區(qū)滿的時(shí)候,就會(huì)執(zhí)行Minor GC羊异。

復(fù)制算法

Minor GC時(shí)Eden區(qū)和Survivor From區(qū)還存活著的對象會(huì)一次性被復(fù)制到Survivor To區(qū)事秀。然后Eden區(qū)和Survivor From區(qū)的內(nèi)存會(huì)被清空。

之后原來的Survivor From區(qū)就空了,而原來的Survivor To區(qū)就非空野舶。這個(gè)時(shí)候它們的角色就調(diào)換了,空的叫做Survivor To區(qū),非空的叫做Survivor From區(qū)易迹。

這種垃圾收集算法叫做復(fù)制算法,整個(gè)過程如下圖所示:

2.png

為什么需要兩個(gè)Survivor區(qū)

復(fù)制算法的優(yōu)點(diǎn)在于,gc完成之后占用的內(nèi)存會(huì)被整理到一個(gè)連續(xù)的空間中。而空閑的內(nèi)存也是連續(xù)的區(qū)域,不會(huì)造成內(nèi)存碎片平道。

如果只有一個(gè)Eden區(qū)和一個(gè)Survivor區(qū),在Minor GC的時(shí)候,Eden區(qū)的存活對象可以被復(fù)制到Survivor區(qū),這樣Eden區(qū)可以被清空出一個(gè)完整的空閑內(nèi)存區(qū)域睹欲。

而Survivor區(qū)存活的對象要怎么辦呢:

  • 如果直接進(jìn)入老年代∫晃荩可能有些對象經(jīng)過少數(shù)的幾次GC就能被釋放窘疮。但在老年代中GC發(fā)生的頻率比新生代低很多。這樣的話就會(huì)導(dǎo)致老年代的內(nèi)存很快被占滿陆淀。

  • 如果不管存活的對象,只是簡單的清除不可達(dá)的對象。那么Survivor區(qū)就會(huì)產(chǎn)生內(nèi)存碎片

  • 如果進(jìn)行壓縮整理,與從新生代復(fù)制過來的存活對象一起整理到Survivor中某個(gè)連續(xù)的區(qū)域的話,消耗的計(jì)算資源會(huì)比較高先嬉。

所以最簡單的做法就是拿多一個(gè)Survivor To區(qū)出來,Eden區(qū)和Survivor From區(qū)存活的對象會(huì)被連續(xù)的復(fù)制到Survivor To區(qū)的一個(gè)連續(xù)區(qū)域中轧苫。然后將Eden區(qū)和Survivor From區(qū)清空就好。

由于新生代大部分的對象生命周期都很短,所以需要復(fù)制的對象的數(shù)目也不會(huì)很多,所以這是比較高效的做法。

對象進(jìn)入老年代

對象在下面三種情況下,對象進(jìn)入到老年代:

  • 占用內(nèi)存比較大的對象如數(shù)組,在創(chuàng)建的時(shí)候不會(huì)分配到Eden區(qū),而會(huì)被直接分配到老年代

  • 當(dāng)Minor GC時(shí)Survivor To區(qū)已經(jīng)放不下還存活的對象的時(shí)候,這些對象就會(huì)被放到老年代中含懊。

  • 每經(jīng)歷一次Minor GC,對象的年齡會(huì)大一歲身冬。當(dāng)對象的年齡到達(dá)某一個(gè)值,Minor GC的時(shí)候就不會(huì)去到Survivor To區(qū),而會(huì)進(jìn)入老年代。

老年代

在新生代中經(jīng)歷了N次垃圾回收后仍然存活的對象,就會(huì)被放到年老代中岔乔。因此,可以認(rèn)為年老代中存放的都是一些生命周期較長的對象酥筝。

在新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對象死去,只有少量存活,所以選用復(fù)制算法,只需要付出少量對象的復(fù)制成本就能完成收集。而老年代中因?yàn)閷ο蟠婊盥矢叱拧]有額外空間對它進(jìn)行分配擔(dān)保,就需要使用"標(biāo)記-清理"或者"標(biāo)記-整理"算法來進(jìn)行回收嘿歌。

標(biāo)記-清理算法

標(biāo)記-清除算法顧名思義,主要就是兩個(gè)動(dòng)作,一個(gè)是標(biāo)記,另一個(gè)就是清除。首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象茁影。

它的缺點(diǎn)有兩個(gè)

  1. 標(biāo)記與清除的效率都比較低
  2. 標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片

它的執(zhí)行過程如下圖所示:

3.png

發(fā)生在老年代的GC叫做Major GC,通常當(dāng)Minor GC晉升到老年代的大小大于老年代的剩余空間時(shí),Major GC就會(huì)被觸發(fā)宙帝。

出現(xiàn)了Major GC,通常會(huì)伴隨著至少一次的Minor GC(不是絕對的,有些收集器有直接進(jìn)行Major GC的策略)。Major GC的速度一般會(huì)比Minor GC慢10倍以上募闲。

除了Minor GC和Major GC之外,還有一個(gè)Full GC的概念,它們的區(qū)別如下:

  1. Minor GC : 回收新生代的垃圾
  2. Major GC : 回收老年代的垃圾
  3. Full GC : 回收整個(gè)堆的垃圾,包括新生代步脓、老年代、持久代等

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

標(biāo)記過程仍和標(biāo)記-清理算法一樣,但是后續(xù)的步驟不是直接對可回收的對象進(jìn)行清理,而是讓所有存活的對象往一端移動(dòng),然后再清理掉邊界以外的內(nèi)存浩螺。它的過程如下圖所示:

4.png

垃圾搜集器

如果說收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)靴患。Java虛擬機(jī)規(guī)范中對垃圾收集器應(yīng)該如何實(shí)現(xiàn)并沒有任何規(guī)定,所以不同的廠商、不同版本的虛擬機(jī)提供的垃圾收集器都可能有很大差別要出。

下面這張圖列出了JDK1.7 Update 14之后HotSpot虛擬機(jī)所包含的垃圾收集器:

5.png

每種收集器具體的實(shí)現(xiàn)方法這里我就不羅列了,感興趣的同學(xué)可以自行搜索鸳君。

finalize方法

即使在可達(dá)性分析中不可達(dá)的對象,也并非是"非死不可"的。這時(shí)它們只是處于"緩刑"階段,要宣布一個(gè)對象死亡,至少要經(jīng)歷兩次標(biāo)記過程:

  1. 第一次可達(dá)性分析之后,不可達(dá)的對象會(huì)被標(biāo)記出來放到一個(gè)"即將回收"的集合中厨幻。

  2. 被標(biāo)記的對象會(huì)進(jìn)行一次篩選,覆蓋了finalize方法并且finalize方法沒有被調(diào)用過的對象會(huì)放到一個(gè)叫做F-Queue的隊(duì)列中相嵌。虛擬機(jī)會(huì)創(chuàng)建一個(gè)低優(yōu)先級的Finalizer線程去執(zhí)行它。如果一個(gè)對象想逃脫死亡的命運(yùn),只需要在finalize方法中將自己重新連接上引用鏈就可以了,例如將自己賦給某個(gè)類變量或?qū)ο蟮某蓡T變量况脆。

  3. 第二次可達(dá)性分析會(huì)將被重新連接上引用鏈的對象移出"即將回收"的集合饭宾。

  4. 最后將不可達(dá)的對象內(nèi)存回收

這里有兩點(diǎn)需要注意的是:

  • "執(zhí)行"finalize方法指的是虛擬機(jī)會(huì)觸發(fā)這個(gè)方法,但不承諾等待它運(yùn)行結(jié)束,這樣做的原因是防止某個(gè)對象的finalize方法運(yùn)行緩慢或者發(fā)生死循環(huán),導(dǎo)致F-Queue的隊(duì)列其他對象永久等待甚至導(dǎo)致內(nèi)存回收系統(tǒng)崩潰。

  • finalize只有一次被調(diào)用的機(jī)會(huì)格了。如果在finalize中將對象重新連接上引用鏈,只有在對象在第一次即將被回收的時(shí)候,finalize方法會(huì)被調(diào)用看铆。在下一次GC的標(biāo)記過程中,因?yàn)閒inalize方法已經(jīng)被調(diào)用過了,所以就不會(huì)被放到F-Queue的隊(duì)列中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盛末,一起剝皮案震驚了整個(gè)濱河市弹惦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悄但,老刑警劉巖棠隐,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異檐嚣,居然都是意外死亡助泽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗡贺,“玉大人隐解,你說我怎么就攤上這事〗氩牵” “怎么了煞茫?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摄凡。 經(jīng)常有香客問我续徽,道長,這世上最難降的妖魔是什么架谎? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任炸宵,我火速辦了婚禮,結(jié)果婚禮上谷扣,老公的妹妹穿的比我還像新娘土全。我一直安慰自己,他們只是感情好会涎,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布裹匙。 她就那樣靜靜地躺著,像睡著了一般末秃。 火紅的嫁衣襯著肌膚如雪概页。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天练慕,我揣著相機(jī)與錄音惰匙,去河邊找鬼。 笑死铃将,一個(gè)胖子當(dāng)著我的面吹牛项鬼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劲阎,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼绘盟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了悯仙?” 一聲冷哼從身側(cè)響起龄毡,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锡垄,沒想到半個(gè)月后沦零,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡货岭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年路操,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了序攘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寻拂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丈牢,到底是詐尸還是另有隱情祭钉,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布己沛,位于F島的核電站慌核,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏申尼。R本人自食惡果不足惜垮卓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望师幕。 院中可真熱鬧粟按,春花似錦、人聲如沸霹粥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽后控。三九已至庙曙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浩淘,已是汗流浹背捌朴。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留张抄,地道東北人砂蔽。 一個(gè)月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像欣鳖,于是被迫代替她去往敵國和親察皇。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容