JVM 回收機制

深入理解JVM讀書筆記

從半個多世紀(jì)前的Lisp語言開始,垃圾回收機制正式登上歷史舞臺薄坏。但是直到今天新啼,仍然沒有一個完美的垃圾回收方案伤靠。從Java誕生到現(xiàn)如今的Java 8, Java的垃圾回收管理機制也一直在嘗試優(yōu)化戴卜。雖然到今天位置垃圾回收機制并不完美逾条,但是它仍舊是目前人類頂尖智慧的結(jié)晶,其中的一些設(shè)計不得不讓人佩服投剥。

說起到垃圾回收機制师脂,我們不得不思考三個問題:

  • 哪些內(nèi)存需要回收?
  • 如何進行回收?
  • 什么時間進行回收吃警?

一. 哪些內(nèi)存需要回收糕篇?

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

很多教科書中把引用計數(shù)算法作為判斷對象是否存活的算法:“給每個對象添加一個計數(shù)器,當(dāng)使用該引用引用時計數(shù)器值加1酌心,當(dāng)引用失效時計數(shù)器值減1娩缰;當(dāng)某個對象的引用計數(shù)器值為0時,表示該對象可以被回收谒府。
客觀的來說引用技術(shù)算法實現(xiàn)簡單,效率也不低浮毯;但是它卻有一個致命的問題:很難解決對象循環(huán)引用的問題完疫。
在JVM中,循環(huán)引用并不會影響對象的回收债蓝。所以JVM中并不是通過引用技術(shù)算法來判斷對象是否能被回收壳鹤。
//TODO: Code

2. 可達性分析算法

在主流商用語言(Java,C#等)中饰迹,一般是通過可達性分析來判斷對象是否可被回收芳誓。從 “GC Root” 對象作為起點開始搜索,搜索所走過的路徑成為引用鏈啊鸭。如果一個對象從GC Root開始無引用鏈可達锹淌,則表示這個對象是不被使用的,是可被回收的赠制。
那GC Root到底是些什么樣的對象呢赂摆? 在Java中可以做GC Root的對象包括 本文不會對JVM內(nèi)存模型進行解析,如果大家有疑問可以自行參考相關(guān)文章

  • 虛擬機棧中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法區(qū)中常亮引用的對象
  • 本地方法棧中JNI引用的對象

3. finalize() 方法

如果一個對象通過可達性分析算法判定為不可達對象钟些,那么等待它的就只是被殺死的命運嗎烟号?然而并不是,咸魚都有翻身的機會政恍,對象也該有它的機會汪拥!
一個對象如果被判定為“不可達”狀態(tài),系統(tǒng)會對它進行篩選篙耗,判斷對象是否需要執(zhí)行finalize() 方法迫筑。 如果對象沒有覆蓋finalize()方法,或者finalize()方法已經(jīng)被系統(tǒng)調(diào)用過一次 [finalize()方法只會被調(diào)用一次]鹤树,拿它就會被判定為不需要執(zhí)行finalize(),也就失去了最后翻身的機會铣焊。
相反如果對象被判定為有必要執(zhí)行finalize()方法,那么這個對象會被放到一個F-Queue的隊列中罕伯。稍后系統(tǒng)會建立一個優(yōu)先級非常低的線程去調(diào)用對象的finalize()方法曲伊。如果在finalize()方法中,對象重新連接上引用鏈,那么在第二次標(biāo)記時它會被移除“即將被回收”的集合坟募,否則它將被回收岛蚤。
//TODO: code

二. 如何進行回收?

1. 垃圾收集算法

垃圾回收算法有很多種懈糯,每一種算法的細(xì)節(jié)又非常復(fù)雜涤妒。在此我們盡量避免介紹一些算法細(xì)節(jié),只是介紹下主流垃圾回收算法的思想赚哗。

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

法如其名她紫,首先將所有需要回收的對象做標(biāo)記【標(biāo)記過程即是上文提到的標(biāo)記】,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記對象屿储。
標(biāo)記清除算法是最基礎(chǔ)的收集算法贿讹。因為后續(xù)的收集算法都是基于標(biāo)記清除算法,并嘗試解決它的兩個不足:

  • 效率問題够掠,標(biāo)記和清除效率都不高
  • 控件問題民褂,清除后會產(chǎn)生很多不連續(xù)的內(nèi)存碎片

//TODO: Image

1.2 復(fù)制算法

Copy算法很大程度上是為了解決效率問題而誕生的。它將可用內(nèi)存平分為兩部分疯潭。每次只使用其中的一部分赊堪,當(dāng)這部分內(nèi)存將要用完時觸發(fā)回收,之后將存活的對象復(fù)制到另一部分內(nèi)存上并清理掉之前的內(nèi)存竖哩。
它的優(yōu)勢:每次都是對一半的區(qū)域進行回收哭廉;不需要考慮內(nèi)存碎片問題,內(nèi)存分配簡單高效相叁。缺點也很明顯:可用內(nèi)存空間只能是全部內(nèi)存的一半群叶。

復(fù)制算法在實際應(yīng)用中使用的比較多,它常被一些商業(yè)虛擬機用來做對新生代的回收钝荡。但是實際上內(nèi)存分塊并不是按照1:1來分配的街立,因為IBM研究表明98%新生代中的對象都是可被回收的,也就是2%的對象是需要被拷貝的埠通。在復(fù)制算法的升級版本中內(nèi)存被分為一個大的Eden區(qū)和兩個小的Survivor區(qū)赎离,每次使用Eden和一個Survivor區(qū)。當(dāng)GC時端辱,講Eden和Survivor中存活的對象Copy到另一個Survivor區(qū)中梁剔。在HotPot虛擬機中Eden和Survivor的默認(rèn)比例是8:1,也就是新生代的可用內(nèi)存為整個新生代內(nèi)存總量的90%舞蔽。一個Survivor區(qū)可用內(nèi)存只有10%的總?cè)萘咳俨。窍到y(tǒng)并不能保證每次回收完之后剩余對象大小一定在10%以內(nèi)。在此種情況下Survivor區(qū)控件不足時渗柿,系統(tǒng)需要依賴其他內(nèi)存(老年代)進行分配擔(dān)保(Handle Promotion)个盆。
//TODO: Image

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

復(fù)制算法在新生代回收中非常給力脖岛,但是在老年代中對象存活率較高,對象的多次拷貝效率會降低颊亮,并且因為對象存活率比較高柴梆,無法像新生代一樣設(shè)置8:1的比例一般只能按照1:1來設(shè)置,導(dǎo)致老年代內(nèi)存的使用率只能是50%左右终惑。所以在老年代中一般不會直接使用復(fù)制算法绍在。
標(biāo)記清理算法正是專門針對老年代的特點而產(chǎn)生的。它跟標(biāo)記-清除算法類似雹有。只是標(biāo)記完成后不是直接回收清除偿渡,而是讓所有存活的對象都像一端移動,然后直接清理其他所有空間霸奕。

1.4 分代收集算法

當(dāng)前普遍使用的分代收集算法并不能算是一個獨立的算法卸察。它更像是一種機制。它只是根據(jù)對象存活周期的不同將內(nèi)存分為新生代和老年代铅祸。而新生代和老年代具體的內(nèi)存回收算法則是前面介紹的算法來實現(xiàn)。

2. 垃圾收集器

垃圾收集器是JVM中對垃圾回收算法的具體實現(xiàn)合武。不同廠商临梗,不同的虛擬機版本所提供的垃圾收集器都可能有很大的不同,我們只對其中比較有代表性的幾個垃圾收集器做介紹稼跳。

2.1 Serial盟庞、Serial Old

Serial收集器是最基本,歷史最悠久的收集器汤善,一般用于分代收集中的新生代收集什猖。見名知意,它是一個單線程的垃圾收集器红淡。并且Serial還有一層含義是不狮,它在進行垃圾回收時必須停止其他所有工作線程(Stop The World),然后使用Copy算法進行垃圾回收在旱,最后恢復(fù)其他工作線程摇零。

Serial Old是Serial的老年代版本桶蝎。它跟Serial機制差不多,同樣是一個單線程收集器登渣,不過它是通過標(biāo)記-整理算法來實現(xiàn)的。

2.2 ParNew胜茧、Parallel Scavenge粘优、Parallel Old

Parallel系列收集器都是通過多條線程進行垃圾回收的。
ParNew與Serial相比除了使用多線程外敬飒,并沒有太多的創(chuàng)新之處(Copy算法)。它是很多Server模式虛擬機的新生代收集器无拗,因為它能很好的與老年代的CMS收集器配合工作。
Parallel Scavenge收集器跟ParNew很像英染,使用Copy算法多線程回收。它的特點是它是一個吞吐量優(yōu)先收集器四康,吞吐量=運行用戶代碼時間/(運行用戶代碼時間+GC時間)搪搏。Parallel Scavenge提供的參數(shù)可以控制Eden和Survivor的空間大小和比例疯溺,進而控制每次GC所用時間和系統(tǒng)總吞吐量。
Parallel Old收集器是Parallel Scavenge的老年代版本哎垦。它使用標(biāo)記整理算法多線程回收囱嫩。它一般用作吞吐量優(yōu)先收集器的老年代版本,跟Parallel Scavenge搭配自稱吞吐量優(yōu)先組合漏设。

2.3 CMS

CMS(Concurrent Mark Sweep)收集器是一種為了盡量縮短停頓時間的收集器墨闲。為了追求更好的用戶體驗,對服務(wù)響應(yīng)速度的要求也會相應(yīng)的提高郑口。CMS也就應(yīng)運而生鸳碧。 CMS采用MS(Mark-Sweep)標(biāo)記清除算法進行垃圾回收。CMS回收過程分為四個階段:

  • 初始標(biāo)記
  • 并發(fā)標(biāo)記
  • 重新標(biāo)記
  • 并發(fā)清除

其中初始標(biāo)記和重新標(biāo)記仍然需要Stop The World犬性。初始標(biāo)記僅僅是標(biāo)記GC Root直接關(guān)聯(lián)到的對象船响,速度很快迅办。并發(fā)標(biāo)記就是做GC Root引用鏈的搜索跑筝,重新標(biāo)記階段是為了糾正并發(fā)階段期間可能由于代碼執(zhí)行導(dǎo)致的標(biāo)記變化南捂。耗時最長的并發(fā)標(biāo)記和并發(fā)清除階段都可以與用戶工作線程并發(fā)執(zhí)行,所以總體來看CMS基本上是與用戶線程并發(fā)執(zhí)行的缸兔。
當(dāng)然CMS并不是一個完美的垃圾收集器日裙。它的并發(fā)會讓它對CPU資源敏感,CPU數(shù)較少時GC會占用比較多的CPU資源惰蜜。另一方面由于是基于標(biāo)記清除算法昂拂,它只能通過Full GC(收集整個堆,不管新生代老年代)對內(nèi)存空間碎片進行處理抛猖。

2.4 G1

G1(Garbage-First) 收集器是當(dāng)今收集器技術(shù)發(fā)展的最前沿成果之一格侯。但是因為是從JDK7u4版本開始移除的Experimental標(biāo)記鼻听,目前在實際生產(chǎn)環(huán)境中使用并不多。

其他的收集器收集的范圍都是整個新生代或者老年代联四。而G1將內(nèi)存區(qū)進一步劃分為若干個大小相等的獨立區(qū)域(Region)撑碴。而新生代和老年代不再是物理隔離的,而分別是一部分不需要連續(xù)性的Region的集合朝墩。G1可以跟蹤并維護每個Region的回收價值(回收所得空間大小與回收所費時間的經(jīng)驗值)醉拓,每次回收優(yōu)先回收價值最大的區(qū)域,也也就是Garbage-First的由來收苏。

G1收集器收集過程也是分為4個階段亿卤,前三個階段與CMS相同,最后階段由并發(fā)清除變?yōu)楹Y選回收鹿霸。所謂篩選回收就是計算Region的回收價值并排序排吴,選擇價值最高的Region進行回收。

它與CMS等其他收集器相比有以下的一些優(yōu)勢:

  • 并行與并發(fā):充分利用多核或多CPU技術(shù)來縮短Stop The World 的時間懦鼠。
  • 分代收集: G1可以獨立管理新老代钻哩,也可以與某些其他收集器配合使用。
  • 空間整合: 與CMS的標(biāo)記-清理算法不同肛冶,整體來看G1是基于標(biāo)記-整理算法的收集器街氢,從局部來看Region間是基于Copy算法實現(xiàn)的。無論如何都是不會產(chǎn)生內(nèi)存碎片的淑趾。
  • 可預(yù)測的停頓:G1和CMS都致力于降低停頓時間。G1顯然技高一籌忧陪。G1允許使用者明確指定M時間段內(nèi)扣泊,消耗在GC的時間不得超過N。

三. 垃圾回收的時機

1. 安全點

在GC開始時嘶摊,系統(tǒng)會停頓下來做枚舉根節(jié)點的操作延蟹。虛擬機一般會維護一個數(shù)據(jù)結(jié)構(gòu),來保存當(dāng)前執(zhí)行上下文和全局的引用位置叶堆。在HotSpot虛擬機中是通過OopMap這個數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)的阱飘。在OopMap的幫助下,HotPots可以快速精準(zhǔn)的完成GC Root枚舉虱颗。
但是問題來了:有很多指令都可能導(dǎo)致OopMap的內(nèi)容變化沥匈,如果為每一條指令都生成對應(yīng)的OopMap會占用大量的額外空間成本。事實上HotPots也不會為每條指令生成OopMap忘渔,只是在“某些特定位置”生成了這些信息高帖。這些位置就是所謂的安全點,也就是系統(tǒng)并不會隨時隨地在任何地方都能停頓下來做GC操作畦粮,只有在安全點才能散址。
安全點太少會導(dǎo)致GC等待時間過長乖阵,安全點過多會增加系統(tǒng)運行成本预麸。那如何選擇安全點呢? 安全點的選擇一般以“是否有讓程序長時間運行的特征”為標(biāo)準(zhǔn)吏祸。例如在方法調(diào)用,循環(huán)跳轉(zhuǎn)犁罩,異常跳轉(zhuǎn)等地方才會有安全點。
為了保證GC發(fā)生時所有線程都能在安全點床估,系統(tǒng)提供搶先式中斷和主動式中斷來實現(xiàn)含滴。搶先式中斷就是所有線程先中斷丐巫,然后讓不在安全點的線程恢復(fù)并運行到安全點。主動式中斷就是系統(tǒng)不直接中斷線程而是只設(shè)置一個標(biāo)記位递胧,其他線程執(zhí)行時主動輪詢這個標(biāo)記位。

2. 安全區(qū)域

安全點解決了如何進入GC的問題缎脾,保證程序執(zhí)行不長時間都會觸發(fā)一次GC。但是如果程序沒有執(zhí)行呢遗菠,如果線程處于Sleep或Blockd狀態(tài)呢联喘?這種情況就需要安全區(qū)域來解決辙纬。 安全區(qū)域是指引用關(guān)系不會發(fā)生變化的一段代碼片段。在這個區(qū)域的任何地方進行GC都是安全的贺拣。線程進入安全區(qū)域后會先標(biāo)識自己進入了安全區(qū)域,系統(tǒng)GC時就會認(rèn)為此線程是回收安全狀態(tài)譬涡。當(dāng)線程要離開安全區(qū)域時,會檢查系統(tǒng)是否完成了根節(jié)點枚舉才決定是否能離開涡匀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市渊跋,隨后出現(xiàn)的幾起案子着倾,更是在濱河造成了極大的恐慌,老刑警劉巖卡者,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異崇决,居然都是意外死亡,警方通過查閱死者的電腦和手機恒傻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盈厘,“玉大人,你說我怎么就攤上這事官边。” “怎么了注簿?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诡渴。 經(jīng)常有香客問我,道長妄辩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任恩袱,我火速辦了婚禮胶哲,結(jié)果婚禮上畔塔,老公的妹妹穿的比我還像新娘鸯屿。我一直安慰自己,他們只是感情好寄摆,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著婶恼,像睡著了一般柏副。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上割择,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音荔泳,去河邊找鬼。 笑死虐杯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的擎椰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼确憨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了休弃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤塔猾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丈甸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯俗,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡得湘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了淘正。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸿吆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惩淳,到底是詐尸還是另有隱情,我是刑警寧澤乓搬,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布代虾,位于F島的核電站激蹲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏托呕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一项郊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧着降,春花似錦差油、人聲如沸任洞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盅弛,卻和暖如春钱骂,著一層夾襖步出監(jiān)牢的瞬間挪鹏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工讨盒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人返顺。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像创南,于是被迫代替她去往敵國和親伦忠。 傳聞我的和親對象是個殘疾皇子稿辙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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

  • JVM架構(gòu) 當(dāng)一個程序啟動之前气忠,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū))赋咽,執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,646評論 0 7
  • 原文閱讀 前言 這段時間懈怠了,罪過脓匿! 最近看到有同事也開始用上了微信公眾號寫博客了,挺好的~給他們點贊宦赠,這博客我...
    碼農(nóng)戲碼閱讀 5,948評論 2 31
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,392評論 17 311
  • 來自: Android夢想特工隊作者: Aaron主頁: http://www.wxtlife.com/原...
    技術(shù)特工隊閱讀 4,356評論 0 28
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理毡琉,因此不免有一些不準(zhǔn)確的地方,同時不同JDK版本的...
    高廣超閱讀 15,545評論 3 83