一篇文章徹底了解Java垃圾收集(GC)機(jī)制

垃圾收集(Garbage Collection ,GC)害晦,是一個(gè)長(zhǎng)久以來(lái)就被思考的問題设拟,當(dāng)考慮GC的時(shí)候蛮瞄,我們必須思考3件事情:

  • 哪些內(nèi)存需要回收置济?
  • 什么時(shí)候回收解恰?
  • 如何回收?

那么在Java中浙于,我們要怎么來(lái)考慮GC呢护盈?首先回想以下內(nèi)存區(qū)域的劃分,其中程序計(jì)數(shù)器羞酗、本地方法棧腐宋、虛擬機(jī)棧三個(gè)區(qū)域隨線程而生,隨線程釋放,棧中的棧幀隨著方法的進(jìn)入和退出執(zhí)行著出棧和入棧的操作胸竞,每一個(gè)棧幀分配多少內(nèi)存基本是在類結(jié)構(gòu)確定時(shí)就已經(jīng)固定的(可能會(huì)進(jìn)行一些優(yōu)化欺嗤,但是大體上已知),因此這幾個(gè)區(qū)域就不需要考慮回收的問題卫枝,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí)煎饼,內(nèi)存自然都被回收。不需要額外的GC算法等校赤。

然而Java堆和方法區(qū)則不一樣吆玖,一個(gè)接口所對(duì)應(yīng)的多個(gè)實(shí)現(xiàn)類所需要的內(nèi)存可能不一樣,一個(gè)方法中的多個(gè)分支所需要的內(nèi)存也可能不一樣痒谴,我們只有在程序處于運(yùn)行期間才能知道程序需要?jiǎng)?chuàng)建那些對(duì)象衰伯,這部分的內(nèi)存的分配和回收是動(dòng)態(tài)的,因此积蔚,垃圾收集器關(guān)注的是這方面的內(nèi)存意鲸。

一. 如何確定對(duì)象可以回收

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

最容易想到與理解的算法,即對(duì)于每一個(gè)對(duì)象尽爆,每當(dāng)該對(duì)象被引用時(shí)怎顾,計(jì)數(shù)器值就+1,引用失效時(shí)漱贱,計(jì)數(shù)器就-1槐雾。因此,當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí)幅狮,即為不可再被使用的募强。該算法也在一些領(lǐng)域被使用來(lái)進(jìn)行內(nèi)存管理,但是JAVA虛擬機(jī)中并沒有選用該算法崇摄。主要是因?yàn)椴荒芎芎玫慕鉀Q循環(huán)引用的問題擎值。

舉個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明循環(huán)引用:

class Container{    public Object obj ;
}public class ReferTest {    public static void main(String[] args){
        Container c1 =new Container();
        Container c2 =new Container();
        c1.obj = c2 ;
        c2.obj = c1 ;
        
        c1 = null ;
        c2 = null ;        //此時(shí)c1 c1會(huì)被判定為死亡對(duì)象么?    }
}

事實(shí)上會(huì)被判定為死亡對(duì)象逐抑,因?yàn)镴AVA虛擬機(jī)不是采用引用計(jì)數(shù)來(lái)進(jìn)行判斷的鸠儿,因此如果發(fā)生垃圾回收,c1厕氨,c2 都會(huì)被回收內(nèi)存进每。

2.可達(dá)性分析

Java、C#的主流實(shí)現(xiàn)都是采用該種方式命斧,來(lái)判斷對(duì)象是否存活田晚。

這個(gè)算法的基本思路就是一系列“GC Roots”作為起始點(diǎn),從這些節(jié)點(diǎn)向下搜索国葬,搜索到的所有引用鏈中的對(duì)象都是可達(dá)的肉瓦,其余的對(duì)象都是不可達(dá)的遭京,如上例,即使c1,c2互相引用泞莉,但是c1,c2都不屬于GC Roots對(duì)象,因此都不可達(dá)船殉。

Java中鲫趁,以下幾種對(duì)象可以作為GC Roots:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
  • 本地方法棧JNI方法引用的對(duì)象利虫。
  • 方法區(qū)類的靜態(tài)屬性引用的對(duì)象挨厚。
  • 方法區(qū)常量引用的對(duì)象。
3.引用的分類

了解了GC Roots之后糠惫,我們可能會(huì)希望存在這么一種對(duì)象疫剃,內(nèi)存夠的時(shí)候不進(jìn)行回收,當(dāng)需要內(nèi)存時(shí)再將其回收硼讽。JDK 1.2 中對(duì)引用進(jìn)行了擴(kuò)充巢价。將引用分為了4種,從強(qiáng)到弱依次為;

強(qiáng)引用(Strong Reference)

我們一般情況下使用的都是強(qiáng)引用固阁,如Object o = new Object()壤躲,之類的代碼。只要強(qiáng)引用還在备燃,垃圾收集器就永遠(yuǎn)不會(huì)回收被引用的對(duì)象碉克。

軟引用(Soft Reference)

SoftReference類來(lái)實(shí)現(xiàn),用來(lái)描述一些還有用但是不必須的對(duì)象并齐,在系統(tǒng)如果不回收就會(huì)發(fā)生OOM時(shí)才會(huì)對(duì)軟引用進(jìn)行內(nèi)存回收漏麦。

弱引用(Weak Reference)

WeakReference類來(lái)實(shí)現(xiàn),描述非必需的對(duì)象况褪,強(qiáng)度弱撕贞,只能活到下一次發(fā)生垃圾回收前,無(wú)論那時(shí)內(nèi)存是否短缺窝剖,都會(huì)對(duì)軟引用對(duì)象進(jìn)行內(nèi)存回收

虛引用(Phantom Reference)

PhantomReference類實(shí)現(xiàn)麻掸,不會(huì)對(duì)生存時(shí)間發(fā)生任何影響,唯一目的時(shí)能在這個(gè)對(duì)象被收集器回收時(shí)得到一個(gè)通知赐纱。

4.其他

及其不建議使用finalize()方法,雖然可以在回收時(shí)被調(diào)用脊奋,但是finalize()方法的執(zhí)行代價(jià)高昂,不確定性大疙描,無(wú)法保證各個(gè)對(duì)象的調(diào)用順序诚隙。使用finalize()能做的工作,使用try()finally()或其他方式可以執(zhí)行的更好起胰。大家可以忘記JAVA中有這個(gè)方法的存在久又。本身就是在JAVA剛誕生時(shí)向C/C++程序員做的妥協(xié)巫延,但是未得到優(yōu)化。

方法區(qū)(永久代)進(jìn)行GC的效率極低地消,花費(fèi)較大炉峰,但是在大量使用反射、動(dòng)態(tài)代理等場(chǎng)景都需要虛擬機(jī)具備類卸載的功能脉执,以保證永生代的空間疼阔。

二.垃圾收集算法

1.標(biāo)記清除算法(Mark-Sweep)

算法分為兩個(gè)階段,標(biāo)記與清除半夷。

標(biāo)記階段:標(biāo)記出所有需要回收的對(duì)象婆廊。回收階段:將所有標(biāo)記區(qū)域回收巫橄。由于該算法不對(duì)空間進(jìn)行整理淘邻,因此會(huì)產(chǎn)生大量的內(nèi)存碎片,內(nèi)存空間碎片過多會(huì)導(dǎo)致在分配較大的對(duì)象時(shí)湘换,因?yàn)闆]有連續(xù)的內(nèi)存而不得不提前觸發(fā)一個(gè)GC宾舅。另外,標(biāo)記與清除的過程效率都不高枚尼。這也是最基礎(chǔ)的GC算法贴浙。

2.復(fù)制算法(Copying)

將內(nèi)存的總?cè)萘糠譃閮蓧K,每次只使用其中的一塊署恍,當(dāng)這一塊用完了崎溃,觸發(fā)GC,此時(shí)將還存活的對(duì)象轉(zhuǎn)移到另一塊內(nèi)存中盯质,之前使用的那一塊內(nèi)存完全清理掉袁串。這樣每次對(duì)一個(gè)半?yún)^(qū)進(jìn)行回收,也不會(huì)存在內(nèi)存碎片呼巷,實(shí)現(xiàn)簡(jiǎn)單囱修,運(yùn)行高效,但是一次只能使用半塊內(nèi)存可能會(huì)造成浪費(fèi)王悍。

在新生代中破镰,絕大部分的對(duì)象時(shí)“朝生夕死”的,因此压储,不需要按照1:1來(lái)劃分空間鲜漩。而是將內(nèi)存分為一塊較大的Eden區(qū)以及兩個(gè)Survivor區(qū),HotSpot虛擬機(jī)中集惋,Eden:Survivor=8:1 孕似,每次使用一個(gè)Eden區(qū)以及一個(gè)Survivor區(qū),90%的空間刮刑,觸發(fā)GC后喉祭,將剩余的對(duì)象轉(zhuǎn)移到未使用的Survivor中养渴,然后清理Eden區(qū)和用過的Survivor區(qū),空間不夠時(shí)泛烙,會(huì)擔(dān)保分配到老年代理卑。這樣一次可以使用90%的內(nèi)存空間,極大的提高了內(nèi)存的使用率胶惰。因此傻工,新生代一般采用這種算法來(lái)回收。

3.標(biāo)記整理算法(Mark-Compact)

如果回收時(shí)空間內(nèi)的對(duì)象存活率較高孵滞,那么使用復(fù)制算法一次只能使用50%的空間(以應(yīng)對(duì)所有對(duì)象都存活的情況),因此老年代采用標(biāo)記整理算法鸯匹。先對(duì)需要清理的對(duì)象進(jìn)行標(biāo)記坊饶,然后將存活的對(duì)象都向一端移動(dòng),直接清理掉端邊界以外的內(nèi)存殴蓬。這種方式也不會(huì)留下內(nèi)存碎片匿级。

標(biāo)記整理算法沒有復(fù)制算法快。

三. Java垃圾收集器

(了解即可染厅,需要時(shí)可以網(wǎng)上細(xì)查)

新生代收集器:Serial收集器痘绎、ParNew收集器(Serial的多線程版本)、Parallel Scanvenge收集器(控制吞吐量肖粮,提高相應(yīng)速度)

老年代收集器:Serial Old收集器孤页、Parallel Old收集器、CMS收集器(最短停頓)涩馆、G1(新生代行施、老年代都可回收)

四. 內(nèi)存的分配與回收

新生代:即復(fù)制算法中提到的Eden區(qū)以及2個(gè)Survivor區(qū)。

老年代:新生代存活足夠長(zhǎng)時(shí)間后進(jìn)入老年代魂那。堆上的另一塊區(qū)域蛾号。

Minor GC:發(fā)生在新生代的垃圾收集動(dòng)作。因?yàn)镴ava對(duì)象存活時(shí)間一般較短涯雅,故Minor GC非常頻繁鲜结,一般回收速度也較快。

Full GC:發(fā)生在老年代的垃圾收集動(dòng)作活逆,伴隨著最少一次的Minor GC精刷,且速度較慢(比Minor GC慢10倍以上)

1.空間的分配

1)對(duì)象優(yōu)先在新生代Eden區(qū)分配。當(dāng)Eden區(qū)沒有足夠空間時(shí)划乖,將發(fā)動(dòng)一次Minor GC.

2)較大對(duì)象需要連續(xù)的空間贬养,如長(zhǎng)字符串或數(shù)組,如果放在新生代會(huì)提前觸發(fā)GC琴庵。故大對(duì)象直接進(jìn)入老年代區(qū)域误算,避免頻繁的GC仰美。

3)長(zhǎng)期存活的對(duì)象進(jìn)入老年代,每個(gè)對(duì)象有一個(gè)年齡儿礼,在對(duì)象頭Mark Word中記錄咖杂,剛被創(chuàng)建時(shí)年齡為0,當(dāng)它活過一次Minor GC蚊夫,并且轉(zhuǎn)移到Survivor中诉字,年齡變?yōu)?,此后,在Survivor區(qū)中每活過一個(gè)Minor GC,年齡就會(huì)+1悦污,當(dāng)年齡達(dá)到某個(gè)程度(默認(rèn)為15)践樱,就會(huì)晉升到老年代。

4)此外,為了適應(yīng)內(nèi)存的復(fù)雜情況,年齡不一定達(dá)到規(guī)定值才能進(jìn)入老年代。當(dāng)Survivor區(qū)的相同年齡所有對(duì)象大小大于Survivor區(qū)大小的一半時(shí)冲杀,此年齡就會(huì)被作為判定標(biāo)準(zhǔn),大于等于該年齡的都會(huì)進(jìn)入老年代睹酌。

2.空間的回收--GC

這里我用一張圖來(lái)徹底解釋清除:

需要解釋的地方有:擔(dān)保失敗权谁,這個(gè)的作用在圖上已經(jīng)解釋的很清楚了,可以在JVM參數(shù)設(shè)置憋沿。

另外一個(gè)地方就是平均大小來(lái)作比較旺芽,因?yàn)橛卸嗌賹?duì)象晉升到老年代是無(wú)法知道的,所以只好取之前每一次晉升到老年代的對(duì)象的容量的平均值大小來(lái)作為經(jīng)驗(yàn)值卤妒,來(lái)決定是否進(jìn)行Full GC來(lái)讓老年代騰出更多空間甥绿。如果仍然失敗,那么只能進(jìn)行一次Full GC则披。在我個(gè)人開來(lái)共缕,之所以使用擔(dān)保,經(jīng)驗(yàn)值來(lái)盡可能的只進(jìn)行MinorGC士复,所有的一切图谷,都是為了盡可能不執(zhí)行Full GC的情況下將需要申請(qǐng)的內(nèi)存空間搞定

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末阱洪,一起剝皮案震驚了整個(gè)濱河市便贵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冗荸,老刑警劉巖承璃,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蚌本,居然都是意外死亡盔粹,警方通過查閱死者的電腦和手機(jī)隘梨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)舷嗡,“玉大人轴猎,你說(shuō)我怎么就攤上這事〗眩” “怎么了捻脖?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)中鼠。 經(jīng)常有香客問我可婶,道長(zhǎng),這世上最難降的妖魔是什么援雇? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任扰肌,我火速辦了婚禮,結(jié)果婚禮上熊杨,老公的妹妹穿的比我還像新娘。我一直安慰自己盗舰,他們只是感情好晶府,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钻趋,像睡著了一般川陆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛮位,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天较沪,我揣著相機(jī)與錄音,去河邊找鬼失仁。 笑死尸曼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萄焦。 我是一名探鬼主播控轿,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拂封!你這毒婦竟也來(lái)了茬射?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冒签,失蹤者是張志新(化名)和其女友劉穎在抛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萧恕,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刚梭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年肠阱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片望浩。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辖所,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出磨德,到底是詐尸還是另有隱情缘回,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布典挑,位于F島的核電站酥宴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏您觉。R本人自食惡果不足惜拙寡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琳水。 院中可真熱鬧肆糕,春花似錦、人聲如沸在孝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)私沮。三九已至始赎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仔燕,已是汗流浹背造垛。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晰搀,地道東北人五辽。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像厕隧,于是被迫代替她去往敵國(guó)和親奔脐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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