一張圖讓你看懂JVM之垃圾回收算法詳解

前言

從上面這個(gè)圖我們總體上對(duì)JVM的結(jié)構(gòu)特別是內(nèi)存結(jié)構(gòu)有了比較清晰的認(rèn)識(shí)瓜浸,雖然在JDK1.8+的版本中拂酣,JVM內(nèi)存管理結(jié)構(gòu)有了一定的優(yōu)化調(diào)整秋冰。主要是方法區(qū)(持久代)取消變成了直接使用元數(shù)據(jù)區(qū)(直接內(nèi)存)的方式,但是整體上JVM的結(jié)構(gòu)并沒(méi)有大改婶熬,特別是我們最為關(guān)心的堆內(nèi)存管理方式并沒(méi)有在JDK1.8+的版本中有什么變化剑勾,所以圖中的結(jié)構(gòu)整體上是沒(méi)有什么不準(zhǔn)確的,之所以將方法區(qū)以及持久代標(biāo)注出來(lái)赵颅,主要還是為了起到對(duì)比認(rèn)識(shí)的作用虽另,大家知道就可以了。

關(guān)于持久代元數(shù)據(jù)區(qū)的使用問(wèn)題饺谬,目前可以理解就是使用的物理內(nèi)存捂刺,理論上是不受JVM自動(dòng)內(nèi)存回收機(jī)制管理的,如果不設(shè)置參數(shù)大小默認(rèn)最大使用限制就是操作系統(tǒng)可用物理內(nèi)存的大小募寨,設(shè)置了-XX:MetaspaceSize參數(shù)的話族展,JVM就會(huì)在使用物理內(nèi)存空間時(shí)自己進(jìn)行限制。

至于直接內(nèi)存與物理內(nèi)存到底是不是一回事拔鹰,我認(rèn)為對(duì)于我們理解上沒(méi)有區(qū)別仪缸,只是概念的區(qū)別,另外就是對(duì)這塊內(nèi)存使用細(xì)節(jié)上的區(qū)別列肢,如果不受JVM的自動(dòng)回收管理恰画,那么怎么管理呢?說(shuō)到底還是JVM本身在直接使用物理內(nèi)存或者說(shuō)是直接內(nèi)存(用時(shí)直接“malloc”物理內(nèi)存區(qū)域瓷马,而不再是JVM進(jìn)程啟動(dòng)時(shí)初始化的內(nèi)存區(qū)域)拴还,還有一種概念叫native memory,說(shuō)實(shí)話我暫時(shí)還不理解他們到底有啥區(qū)別决采,如果大家對(duì)這些概念有更好的認(rèn)識(shí)自沧,也可以給我留言哦!之所以對(duì)這幾個(gè)問(wèn)題做一些筆墨的說(shuō)明树瞭,主要是在之前的文章中大家對(duì)此提出了疑問(wèn)拇厢,所以正好在這節(jié)的內(nèi)容中進(jìn)行下闡述。

回到今天的主題晒喷,我們知道JAVA最大的優(yōu)點(diǎn)就是可以實(shí)現(xiàn)自動(dòng)內(nèi)存管理孝偎,這極大的便利了JAVA程序員,降低了使用成本凉敲。但這也使得平時(shí)我們?cè)谑褂肑AVA編程時(shí)不太關(guān)注JVM到底是怎樣進(jìn)行內(nèi)存回收的衣盾,只有在需要實(shí)際對(duì)JVM進(jìn)行系統(tǒng)性能調(diào)優(yōu)寺旺,這里的場(chǎng)景可能是在系統(tǒng)面臨極致性能優(yōu)化要求時(shí),我們才發(fā)現(xiàn)需要對(duì)JAVA的整體內(nèi)存結(jié)構(gòu)以及內(nèi)存回收機(jī)制要有一定的認(rèn)識(shí)和了解才行势决。

從上面的圖中阻塑,我們也大致對(duì)整個(gè)垃圾回收系統(tǒng)進(jìn)行了標(biāo)注,這里主要涉及回收策略果复、回收算法陈莽、垃圾回收器這幾個(gè)部分。形象一點(diǎn)表述虽抄,就是JVM需要知道那些內(nèi)存可以被回收走搁,要有一套識(shí)別機(jī)制,在知道那些內(nèi)存可以回收以后具體采用什么樣的回收方式迈窟,這就需要設(shè)計(jì)一些回收算法私植,而具體的垃圾回收器就是根據(jù)不同內(nèi)存區(qū)域的使用特點(diǎn),采用相應(yīng)地回收策略和算法的具體實(shí)現(xiàn)了车酣。

在??圖中曲稼,我們也標(biāo)注了不同垃圾回收器所適用的特定內(nèi)存區(qū)域,對(duì)于JVM垃圾回收這塊的優(yōu)化湖员,就是我們需要在了解這些垃圾回收算法躯肌、垃圾回收器特點(diǎn)后能夠根據(jù)自己應(yīng)用的場(chǎng)景選擇合適的垃圾收集器,以及各區(qū)域垃圾收集器的搭配關(guān)系破衔。下面我們就從這幾個(gè)方面給大家介紹,JVM的垃圾回收相關(guān)的知識(shí)點(diǎn)钱烟。

回收策略

我們知道晰筛,JVM進(jìn)行內(nèi)存回收的主要目的是為了回收不再使用的內(nèi)存,因?yàn)樵谶M(jìn)行JAVA程序編寫時(shí)拴袭,我們只有new的操作,而不需要收工釋放不再使用的空間读第,如果這些空閑內(nèi)存不能及時(shí)被回收,很快我們的JVM內(nèi)存空間就會(huì)泄露(新申請(qǐng)內(nèi)存空間的操作失敗拥刻,導(dǎo)致程序報(bào)錯(cuò))怜瞒,所以回收不再使用的內(nèi)存的目的則是為了及時(shí)釋放空間,騰籠換鳥般哼,以防止內(nèi)存泄漏吴汪。

那么問(wèn)題來(lái)了,JAVA程序申請(qǐng)了那么多的內(nèi)存空間蒸眠,那些內(nèi)存才能被認(rèn)定是不再使用的內(nèi)存呢漾橙?搞錯(cuò)了,如果把正在被程序使用的內(nèi)存給釋放了楞卡,程序邏輯就空指針異常了霜运!

我們知道在JVM中內(nèi)存分配的基本粒度主要是對(duì)象脾歇、基本類型。而基本類型的使用主要是包括在對(duì)象中的局部變量淘捡,所以回收對(duì)象所占用的內(nèi)存是JAVA垃圾回收的主要目標(biāo)藕各。

那么如何判斷對(duì)象是處于可回收狀態(tài)的呢?在主流的JVM中是采用“可達(dá)性分析算法”來(lái)進(jìn)行判斷的焦除。

這個(gè)算法的基本思路就是通過(guò)一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn)激况,并從這些節(jié)點(diǎn)開始往下進(jìn)行搜索,搜索走過(guò)的路徑我們稱之為引用鏈(Reference Chain)踢京,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí)誉碴,我們就稱之為對(duì)象引用不可達(dá),則證明這個(gè)對(duì)象是不可用的瓣距,就可以暫時(shí)判定這個(gè)對(duì)象為可回收對(duì)象黔帕。示意圖如下:

在圖中雖然Obj F與Obj J之間互相有關(guān)聯(lián)但是它們到GC Roots是不可達(dá)的,所以將會(huì)被判定為可回收對(duì)象蹈丸。既然如此成黄,什么樣的對(duì)象可以作為GC Roots對(duì)象呢?

在JAVA中可以被作為GC Roots的對(duì)象主要是:虛擬機(jī)棧-棧幀中的本地變量表所引用的對(duì)象逻杖、方法區(qū)(<JDK1.8)中類靜態(tài)屬性所引用的對(duì)象/常量屬性所引用的對(duì)象奋岁、本地方法棧中引用的對(duì)象。

這里還需要注意一個(gè)小的細(xì)節(jié)荸百,就是被判定為對(duì)象不可達(dá)的對(duì)象也并非會(huì)被立刻回收闻伶,在學(xué)習(xí)JAVA語(yǔ)法是我們應(yīng)該學(xué)習(xí)過(guò)finalize()方法,如果對(duì)象重寫了finalize方法够话,并重新把this關(guān)鍵字賦值給了某個(gè)類變量或?qū)ο蟮某蓡T變量的話蓝翰,該對(duì)象就會(huì)被"救活",具體過(guò)程可參考上圖所示女嘲,只是這種方式并不鼓勵(lì)大家使用畜份,了解下就行。

在關(guān)于如何判定對(duì)象是否屬于不再使用的內(nèi)存時(shí)欣尼,還有個(gè)通常會(huì)被大家錯(cuò)誤認(rèn)為是JVM使用的方式-“引用計(jì)數(shù)法”爆雹,事實(shí)上引用計(jì)數(shù)法的實(shí)現(xiàn)比較簡(jiǎn)單,判定效率也比較高愕鼓,在Python語(yǔ)言中就使用了這種算法進(jìn)行內(nèi)存管理钙态,但是它有一個(gè)比較難解決的對(duì)象之間循環(huán)引用的問(wèn)題,所以在JAVA虛擬機(jī)里并沒(méi)有選用“引用計(jì)數(shù)法”來(lái)管理內(nèi)存菇晃。這個(gè)問(wèn)題很多人都會(huì)搞錯(cuò)驯绎,包括有很多年開發(fā)經(jīng)驗(yàn)的程序員,需要大家注意下谋旦!

回收算法

在JVM中主要的垃圾收集算法有:標(biāo)記-清除剩失、標(biāo)記-清除-壓縮(簡(jiǎn)稱“標(biāo)記-整理”)屈尼、標(biāo)記-復(fù)制-清除(簡(jiǎn)稱“復(fù)制”、分代收集算法拴孤。這幾種收集算法互相配合脾歧,針對(duì)不同的內(nèi)存區(qū)域采取對(duì)應(yīng)的收集算法實(shí)現(xiàn)(這里具體是由相應(yīng)的垃圾收集器實(shí)現(xiàn))

下面我們就分別來(lái)看下這幾種收集算法的特點(diǎn):

1)演熟、標(biāo)記-清除

標(biāo)記-清除算法是最為基礎(chǔ)的一種收集算法鞭执,算法分為:“標(biāo)記”和“清除”兩個(gè)階段。首先標(biāo)記出所有需要回收的對(duì)象(標(biāo)記的過(guò)程就是上面介紹過(guò)的根節(jié)點(diǎn)可達(dá)算法)芒粹,在標(biāo)記完后統(tǒng)一回收所有被標(biāo)記對(duì)象占用的內(nèi)存空間兄纺。

示意圖如下:

這種收集算法的優(yōu)點(diǎn)是簡(jiǎn)單直接,不會(huì)影響JVM進(jìn)程的正常運(yùn)行化漆。而其缺點(diǎn)也是非常明顯估脆,首先,這樣的回收方式會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片座云,不利于后續(xù)連續(xù)內(nèi)存的分配疙赠;其次,這種方式的效率也不高朦拖。

2)圃阳、標(biāo)記-復(fù)制-清除

這種算法的思路是將可用的內(nèi)存空間按容量劃分為大小相等的兩塊,每次只使用其中一塊璧帝。當(dāng)這一塊使用完了捍岳,就將還存活著的對(duì)象復(fù)制到另外一塊上面(移動(dòng)堆頂指針,按順序分配內(nèi)存)睬隶,然后再把已使用過(guò)的內(nèi)存空間一次清理掉祟同。

示意圖如下:

這種收集方式比較好的解決了效率和內(nèi)存碎片的問(wèn)題,但是會(huì)浪費(fèi)掉一般的內(nèi)存空間理疙。目前此種算法主要用于新生代回收(文頂?shù)膱D中有標(biāo)注)。

因?yàn)樾律闹?8%的對(duì)象都是很快就需要被回收的對(duì)象泞坦,這一點(diǎn)大家在編程時(shí)可以體會(huì)到窖贤,所以并不需要1:1的比例來(lái)劃分內(nèi)存空間,在新生代中JVM是按照“8:1:1”的比例(文頂圖中有標(biāo)注)來(lái)將整個(gè)新生代內(nèi)存劃分為一塊較大的Eden區(qū)和兩塊較小的Survivor區(qū)(S0贰锁、S1)赃梧。

每次使用Eden區(qū)和其中一個(gè)Survivor區(qū),當(dāng)發(fā)生回收時(shí)將Eden區(qū)和Survivor區(qū)中還存活的對(duì)象一次性復(fù)制到另一塊Survivor區(qū)上豌熄,最后清理掉Eden區(qū)和剛才使用過(guò)的Survivor區(qū)授嘀。理想情況下,每次新生代中的可用空間是整個(gè)新生代容量的90%(80%+10%)锣险,只會(huì)有10%的內(nèi)存會(huì)被浪費(fèi)蹄皱。實(shí)際情況中览闰,如果另外一個(gè)10%的Survivor區(qū)無(wú)法裝下所有還存活的對(duì)象時(shí),就會(huì)將這些對(duì)象直接放入老年代空間中(這塊在后面的分代回收算法會(huì)說(shuō)到巷折,這里先了解下)压鉴。

3)、標(biāo)記-清除-壓縮

如果在對(duì)象存活率較高的情況下锻拘,仍然采用復(fù)制算法的話油吭,因?yàn)橐M(jìn)行較多的復(fù)制操作,效率就會(huì)變得很低署拟,而且如果不想浪費(fèi)50%的內(nèi)存空間的話婉宰,就還需要額外的空間進(jìn)行分配擔(dān)保****,以應(yīng)對(duì)存活對(duì)象超額的情況推穷。顯然老年代不能采用2)中的復(fù)制算法心包。

根據(jù)老年代的特點(diǎn),標(biāo)記-清除-壓縮(簡(jiǎn)稱標(biāo)記-整理)算法應(yīng)運(yùn)而生缨恒,這種算法的標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣谴咸,只是后續(xù)的步驟不再是直接清除可以回收的對(duì)象,而是將所有存活的對(duì)象都向一端移動(dòng)后骗露,再直接清理掉端邊界以外的內(nèi)存岭佳。

示意圖如下:

4)、分代回收算法

實(shí)際上在講解復(fù)制算法時(shí)已經(jīng)涉及到了分代回收的內(nèi)容萧锉,這種算法根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊珊随,Java中主要是新生代、年老代柿隙。這樣就可以根據(jù)各個(gè)年代的特點(diǎn)叶洞,采用合適的收集算法了在文頂?shù)膱D中已經(jīng)標(biāo)示禀崖,新生代采用了復(fù)制算法衩辟,而老年代采用了整理算法這里就不再贅述波附。

垃圾回收器

關(guān)于垃圾回收器部分的內(nèi)容艺晴,由于篇幅的關(guān)系會(huì)在后續(xù)的《一張圖讓你看懂JVM之垃圾回收器詳解》一文中進(jìn)行講解,敬請(qǐng)關(guān)注掸屡!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末封寞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仅财,更是在濱河造成了極大的恐慌狈究,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盏求,死亡現(xiàn)場(chǎng)離奇詭異抖锥,居然都是意外死亡亿眠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門宁改,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缕探,“玉大人,你說(shuō)我怎么就攤上這事还蹲〉模” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵谜喊,是天一觀的道長(zhǎng)潭兽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)斗遏,這世上最難降的妖魔是什么山卦? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮诵次,結(jié)果婚禮上账蓉,老公的妹妹穿的比我還像新娘。我一直安慰自己逾一,他們只是感情好铸本,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遵堵,像睡著了一般箱玷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陌宿,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天锡足,我揣著相機(jī)與錄音,去河邊找鬼壳坪。 笑死舶得,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的爽蝴。 我是一名探鬼主播沐批,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霜瘪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起惧磺,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颖对,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后磨隘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缤底,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顾患,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了个唧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片江解。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖徙歼,靈堂內(nèi)的尸體忽然破棺而出犁河,到底是詐尸還是另有隱情,我是刑警寧澤魄梯,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布桨螺,位于F島的核電站,受9級(jí)特大地震影響酿秸,放射性物質(zhì)發(fā)生泄漏灭翔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一辣苏、第九天 我趴在偏房一處隱蔽的房頂上張望肝箱。 院中可真熱鬧,春花似錦稀蟋、人聲如沸煌张。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)唱矛。三九已至,卻和暖如春井辜,著一層夾襖步出監(jiān)牢的瞬間绎谦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工粥脚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窃肠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓刷允,卻偏偏與公主長(zhǎng)得像冤留,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子树灶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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