BAT直通車:阿里架構(gòu)師詳解JVM垃圾回收機制,看完還不懂那就放棄吧

一、如何判定對象為垃圾對象

在堆里面存放著Java世界中幾乎所有的對象實例, 垃圾收集器在對堆進行回收前, 第一件事就是判斷哪些對象已死(可回收).

1. 引用計數(shù)法

在JDK1.2之前,使用的是引用計數(shù)器算法功蜓。 在對象中添加一個引用計數(shù)器,當(dāng)有地方引用這個對象的時候蕊梧,引用計數(shù)器的值就+1霞赫,當(dāng)引用失效的時候,計數(shù)器的值就-1肥矢,當(dāng)引用計數(shù)器被減為零的時候端衰,標(biāo)志著這個對象已經(jīng)沒有引用了叠洗,可以回收了!

問題:如果在A類中調(diào)用B類的方法旅东,B類中調(diào)用A類的方法灭抑,這樣當(dāng)其他所有的引用都消失了之后,A和B還有一個相互的引用抵代,也就是說兩個對象的引用計數(shù)器各為1腾节,而實際上這兩個對象都已經(jīng)沒有額外的引用,已經(jīng)是垃圾了荤牍。但是該算法并不會計算出該類型的垃圾案腺。

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

在主流商用語言(如Java、C#)的主流實現(xiàn)中, 都是通過可達(dá)性分析算法來判定對象是否存活的: 通過一系列的稱為 GC Roots 的對象作為起點, 然后向下搜索; 搜索所走過的路徑稱為引用鏈/Reference Chain, 當(dāng)一個對象到 GC Roots 沒有任何引用鏈相連時, 即該對象不可達(dá), 也就說明此對象是不可用的, 如下圖:雖然E和F相互關(guān)聯(lián)康吵, 但它們到GC Roots是不可達(dá)的, 因此也會被判定為可回收的對象劈榨。

注: 即使在可達(dá)性分析算法中不可達(dá)的對象, VM也并不是馬上對其回收, 因為要真正宣告一個對象死亡, 至少要經(jīng)歷兩次標(biāo)記過程: 第一次是在可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈, 第二次是GC對在F-Queue執(zhí)行隊列中的對象進行的小規(guī)模標(biāo)記(對象需要覆蓋finalize()方法且沒被調(diào)用過).

在Java, 可作為GC Roots的對象包括:

  • 方法區(qū): 類靜態(tài)屬性引用的對象;
  • 方法區(qū): 常量引用的對象;
  • 虛擬機棧(本地變量表)中引用的對象.
  • 本地方法棧JNI(Native方法)中引用的對象。

二晦嵌、如何回收

回收策略

垃圾收集策略有分代收集和分區(qū)收集同辣。

分代收集算法

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

該算法分為“標(biāo)記”和“清除”兩個階段: 首先標(biāo)記出所有需要回收的對象(可達(dá)性分析), 在標(biāo)記完成后統(tǒng)一清理掉所有被標(biāo)記的對象.

該算法會有兩個問題:

  1. 效率問題,標(biāo)記和清除效率不高惭载。
  2. 空間問題: 標(biāo)記清除后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片, 空間碎片太多可能會導(dǎo)致在運行過程中需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集旱函。

所以它一般用于"垃圾不太多的區(qū)域,比如老年代"描滔。

2. 復(fù)制算法(新生代)

該算法的核心是將可用內(nèi)存按容量劃分為大小相等的兩塊, 每次只用其中一塊, 當(dāng)這一塊的內(nèi)存用完, 就將還存活的對象(非垃圾)復(fù)制到另外一塊上面, 然后把已使用過的內(nèi)存空間一次清理掉.

優(yōu)點:不用考慮碎片問題棒妨,方法簡單高效。 缺點:內(nèi)存浪費嚴(yán)重伴挚。

現(xiàn)代商用VM的新生代均采用復(fù)制算法, 但由于新生代中的98%的對象都是生存周期極短的, 因此并不需完全按照1∶1的比例劃分新生代空間, 而是將新生代劃分為一塊較大的Eden區(qū)和兩塊較小的Survivor區(qū)(HotSpot默認(rèn)Eden和Survivor的大小比例為8∶1), 每次只用Eden和其中一塊Survivor. 當(dāng)發(fā)生MinorGC時, 將Eden和Survivor中還存活著的對象一次性地拷貝到另外一塊Survivor上, 最后清理掉Eden和剛才用過的Survivor的空間. 當(dāng)Survivor空間不夠用(不足以保存尚存活的對象)時, 需要依賴?yán)夏甏M行空間分配擔(dān)保機制, 這部分內(nèi)存直接進入老年代靶衍。

復(fù)制算法的空間分配擔(dān)保: 在執(zhí)行Minor GC前, VM會首先檢查老年代是否有足夠的空間存放新生代尚存活對象, 由于新生代使用復(fù)制收集算法, 為了提升內(nèi)存利用率, 只使用了其中一個Survivor作為輪換備份, 因此當(dāng)出現(xiàn)大量對象在Minor GC后仍然存活的情況時, 就需要老年代進行分配擔(dān)保, 讓Survivor無法容納的對象直接進入老年代, 但前提是老年代需要有足夠的空間容納這些存活對象. 但存活對象的大小在實際完成GC前是無法明確知道的, 因此Minor GC前, VM會先首先檢查老年代連續(xù)空間是否大于新生代對象總大小或歷次晉升的平均大小, 如果條件成立, 則進行Minor GC, 否則進行Full GC(讓老年代騰出更多空間). 然而取歷次晉升的對象的平均大小也是有一定風(fēng)險的, 如果某次Minor GC存活后的對象突增,遠(yuǎn)遠(yuǎn)高于平均值的話,依然可能導(dǎo)致?lián)J?Handle Promotion Failure, 老年代也無法存放這些對象了), 此時就只好在失敗后重新發(fā)起一次Full GC(讓老年代騰出更多空間).

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

標(biāo)記清除算法會產(chǎn)生內(nèi)存碎片問題, 而復(fù)制算法需要有額外的內(nèi)存擔(dān)痹痔浚空間, 于是針對老年代的特點, 又有了標(biāo)記整理算法. 標(biāo)記整理算法的標(biāo)記過程與標(biāo)記清除算法相同, 但后續(xù)步驟不再對可回收對象直接清理, 而是讓所有存活的對象都向一端移動,然后清理掉端邊界以外的內(nèi)存.

4. 方法區(qū)回收(永久代)

在方法區(qū)進行垃圾回收一般”性價比”較低, 因為在方法區(qū)主要回收兩部分內(nèi)容: 廢棄常量和無用的類.

回收廢棄常量與回收其他年代中的對象類似, 但要判斷一個類是否無用則條件相當(dāng)苛刻:

  1. 該類所有的實例都已經(jīng)被回收, Java堆中不存在該類的任何實例;
  2. 該類對應(yīng)的Class對象沒有在任何地方被引用(也就是在任何地方都無法通過反射訪問該類的方法);
  3. 加載該類的ClassLoader已經(jīng)被回收. 但即使?jié)M足以上條件也未必一定會回收, Hotspot VM還提供了-Xnoclassgc參數(shù)控制(關(guān)閉CLASS的垃圾回收功能). 因此在大量使用動態(tài)代理茎芋、CGLib等字節(jié)碼框架的應(yīng)用中一定要關(guān)閉該選項, 開啟VM的類卸載功能, 以保證方法區(qū)不會溢出.

分區(qū)收集

分區(qū)算法則將整個堆空間劃分為連續(xù)的不同小區(qū)間, 每個小區(qū)間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區(qū)間

在相同條件下, 堆空間越大, 一次GC耗時就越長, 從而產(chǎn)生的停頓也越長. 為了更好地控制GC產(chǎn)生的停頓時間, 將一塊大的內(nèi)存區(qū)域分割為多個小塊, 根據(jù)目標(biāo)停頓時間, 每次合理地回收若干個小區(qū)間(而不是整個堆), 從而減少一次GC所產(chǎn)生的停頓

三、垃圾回收器

Serial:Serial收集器是Hotspot運行在Client模式下的默認(rèn)新生代收集器, 它在進行垃圾收集時蜈出,會暫停所有的工作進程田弥,用一個線程去完成GC工作

特點:簡單高效,適合jvm管理內(nèi)存不大的情況(十兆到百兆)铡原。

Parnew:ParNew收集器其實是Serial的多線程版本偷厦,回收策略完全一樣,但是他們又有著不同燕刻。

我們說了Parnew是多線程gc收集只泼,所以它配合多核心的cpu效果更好,如果是一個cpu卵洗,他倆效果就差不多请唱。(可用-XX:ParallelGCThreads參數(shù)控制GC線程數(shù))

Cms:CMS(Concurrent Mark Sweep)收集器是一款具有劃時代意義的收集器, 一款真正意義上的并發(fā)收集器, 雖然現(xiàn)在已經(jīng)有了理論意義上表現(xiàn)更好的G1收集器, 但現(xiàn)在主流互聯(lián)網(wǎng)企業(yè)線上選用的仍是CMS(如Taobao),又稱多并發(fā)低暫停的收集器弥咪。

由他的英文組成可以看出,它是基于標(biāo)記-清除算法實現(xiàn)的十绑。整個過程分4個步驟:

  • 初始標(biāo)記(CMS initial mark):僅只標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象, 速度很快
  • 并發(fā)標(biāo)記(CMS concurrent mark: GC Roots Tracing過程)
  • 重新標(biāo)記(CMS remark):修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運行而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄
  • 并發(fā)清除(CMS concurrent sweep: 已死對象將會就地釋放)

可以看到聚至,初始標(biāo)記、重新標(biāo)記需要STW(stop the world 即:掛起用戶線程)操作本橙。因為最耗時的操作是并發(fā)標(biāo)記和并發(fā)清除扳躬。所以總體上我們認(rèn)為CMS的GC與用戶線程是并發(fā)運行的。

優(yōu)點:并發(fā)收集甚亭、低停頓

缺點:

  • CMS默認(rèn)啟動的回收線程數(shù)=(CPU數(shù)目+3)*4 當(dāng)CPU數(shù)>4時, GC線程最多占用不超過25%的CPU資源, 但是當(dāng)CPU數(shù)<=4時, GC線程可能就會過多的占用用戶CPU資源, 從而導(dǎo)致應(yīng)用程序變慢, 總吞吐量降低.
  • 無法清除浮動垃圾(GC運行到并發(fā)清除階段時用戶線程產(chǎn)生的垃圾)贷币,因為用戶線程是需要內(nèi)存的,如果浮動垃圾施放不及時亏狰,很可能就造成內(nèi)存溢出片择,所以CMS不能像別的垃圾收集器那樣等老年代幾乎滿了才觸發(fā),CMS提供了參數(shù)-XX:CMSInitiatingOccupancyFraction來設(shè)置GC觸發(fā)百分比(1.6后默認(rèn)92%),當(dāng)然我們還得設(shè)置啟用該策略-XX:+UseCMSInitiatingOccupancyOnly
  • 因為CMS采用標(biāo)記-清除算法骚揍,所以可能會帶來很多的碎片字管,如果碎片太多沒有清理,jvm會因為無法分配大對象內(nèi)存而觸發(fā)GC信不,因此CMS提供了-XX:+UseCMSCompactAtFullCollection參數(shù)嘲叔,它會在GC執(zhí)行完后接著進行碎片整理,但是又會有個問題抽活,碎片整理不能并發(fā)硫戈,所以必須單線程去處理,所以如果每次GC完都整理用戶線程stop的時間累積會很長下硕,所以XX:CMSFullGCsBeforeCompaction參數(shù)設(shè)置隔幾次GC進行一次碎片整理(默認(rèn)為0)丁逝。

G1:同優(yōu)秀的CMS垃圾回收器一樣,G1也是關(guān)注最小時延的垃圾回收器梭姓,也同樣適合大尺寸堆內(nèi)存的垃圾收集霜幼,官方也推薦使用G1來代替選擇CMS。G1最大的特點是引入分區(qū)的思路誉尖,弱化分代的概念罪既,合理利用垃圾收集各個周期的資源,解決了其他收集器甚至CMS的眾多缺陷铡恕。

因為每個區(qū)都有E琢感、S、O代探熔,所以在G1中驹针,不需要對整個Eden等代進行回收,而是尋找可回收對象比較多的區(qū)诀艰,然后進行回收(雖然也需要STW操作柬甥,但是花費的時間是很少的)墙牌,保證高效率。

新生代收集

G1的新生代收集跟ParNew類似暗甥,如果存活時間超過某個閾值喜滨,就會被轉(zhuǎn)移到S/O區(qū)。

年輕代內(nèi)存由一組不連續(xù)的heap區(qū)組成, 這種方法使得可以動態(tài)調(diào)整各代區(qū)域的大小

老年代收集

分為以下幾個階段:

  • 初始標(biāo)記 (Initial Mark: Stop the World Event) 在G1中, 該操作附著一次年輕代GC, 以標(biāo)記Survivor中有可能引用到老年代對象的Regions.
  • 掃描根區(qū)域 (Root Region Scanning: 與應(yīng)用程序并發(fā)執(zhí)行) 掃描Survivor中能夠引用到老年代的references. 但必須在Minor GC觸發(fā)前執(zhí)行完
  • 并發(fā)標(biāo)記 (Concurrent Marking : 與應(yīng)用程序并發(fā)執(zhí)行) 在整個堆中查找存活對象, 但該階段可能會被Minor GC中斷
  • 重新標(biāo)記 (Remark : Stop the World Event) 完成堆內(nèi)存中存活對象的標(biāo)記. 使用snapshot-at-the-beginning(SATB, 起始快照)算法, 比CMS所用算法要快得多(空Region直接被移除并回收, 并計算所有區(qū)域的活躍度).
  • 清理 (Cleanup : Stop the World Event and Concurrent) 在含有存活對象和完全空閑的區(qū)域上進行統(tǒng)計(STW)撤防、擦除Remembered Sets(使用Remembered Set來避免掃描全堆虽风,每個區(qū)都有對應(yīng)一個Set用來記錄引用信息、讀寫操作記錄)(STW)寄月、重置空regions并將他們返還給空閑列表(free list)(Concurrent)

# 鏈接 Java程序員福利"常用資料分享"

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辜膝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漾肮,更是在濱河造成了極大的恐慌厂抖,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件克懊,死亡現(xiàn)場離奇詭異忱辅,居然都是意外死亡,警方通過查閱死者的電腦和手機谭溉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門墙懂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萌丈,“玉大人璧瞬,你說我怎么就攤上這事冠桃“逡遥” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵掷漱,是天一觀的道長惹想。 經(jīng)常有香客問我票顾,道長弄匕,這世上最難降的妖魔是什么颅悉? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮粘茄,結(jié)果婚禮上签舞,老公的妹妹穿的比我還像新娘。我一直安慰自己柒瓣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布吠架。 她就那樣靜靜地躺著芙贫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傍药。 梳的紋絲不亂的頭發(fā)上磺平,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天魂仍,我揣著相機與錄音,去河邊找鬼拣挪。 笑死擦酌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菠劝。 我是一名探鬼主播赊舶,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赶诊!你這毒婦竟也來了笼平?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤舔痪,失蹤者是張志新(化名)和其女友劉穎寓调,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锄码,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡夺英,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了滋捶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秋麸。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炬太,靈堂內(nèi)的尸體忽然破棺而出灸蟆,到底是詐尸還是另有隱情,我是刑警寧澤亲族,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布炒考,位于F島的核電站,受9級特大地震影響霎迫,放射性物質(zhì)發(fā)生泄漏斋枢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一知给、第九天 我趴在偏房一處隱蔽的房頂上張望瓤帚。 院中可真熱鬧,春花似錦涩赢、人聲如沸戈次。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怯邪。三九已至,卻和暖如春花墩,著一層夾襖步出監(jiān)牢的瞬間悬秉,已是汗流浹背澄步。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留和泌,地道東北人村缸。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像武氓,于是被迫代替她去往敵國和親梯皿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

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