深入理解JVM虛擬機(jī) - 垃圾收集算法與垃圾收集器

概述

說到垃圾收集(Garbage Collection警没,GC),大部分人都會(huì)把這項(xiàng)技術(shù)當(dāng)做 Java 語言的伴生產(chǎn)物缔刹。事實(shí)上球涛,GC的歷史比Java久遠(yuǎn),1960年誕生于MIT的Lisp是第一門真正使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語言校镐。當(dāng)Lisp還在胚胎時(shí)期時(shí)亿扁,人們就在思考GC需要完成的3件事情:

  • 哪些內(nèi)存需要回收?
  • 什么時(shí)候回收鸟廓?
  • 如何回收从祝?

經(jīng)過半個(gè)多時(shí)間的發(fā)展,目前內(nèi)存的動(dòng)態(tài)分配與內(nèi)存回收技術(shù)已經(jīng)相當(dāng)?shù)某墒煲眨磺锌雌饋矶歼M(jìn)入了“自動(dòng)化”時(shí)代牍陌,那為什么我們還要去了解GC和內(nèi)存分配呢?答案很簡單:當(dāng)需要排查各種內(nèi)存溢出员咽、內(nèi)存泄漏問題時(shí)毒涧,當(dāng)垃圾收集成為系統(tǒng)達(dá)到更高并發(fā)量的瓶頸時(shí),我們就需要對(duì)這些“自動(dòng)化”的技術(shù)實(shí)施必要的監(jiān)控和調(diào)節(jié)骏融。

深入理解JVM虛擬機(jī) - JVM運(yùn)行時(shí)數(shù)據(jù)區(qū) 中链嘀,介紹了JVM內(nèi)存運(yùn)行時(shí)區(qū)域的各個(gè)部分萌狂,其中 程序計(jì)數(shù)器、虛擬機(jī)棧怀泊、本地方法棧3個(gè)區(qū)域隨線程而生茫藏,隨線程而滅,因此在這幾個(gè)區(qū)域內(nèi)就不需要過多考慮回收的問題霹琼,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí)务傲,內(nèi)存自然就跟隨者回收了。而Java 堆和方法區(qū)則不一樣枣申,一個(gè)接口中的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不一樣售葡,一個(gè)方法中的多個(gè)分支需要的內(nèi)存也可能不一樣,我們只有在程序處于運(yùn)行期間時(shí)才能知道會(huì)創(chuàng)建哪些對(duì)象忠藤,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的挟伙,垃圾收集器所關(guān)注的就是這部分內(nèi)存,本文后續(xù)討論中的“內(nèi)存”分配與回收也僅指這一部分內(nèi)存模孩。

如何判定對(duì)象已死亡

對(duì)象“存活”判定算法

在堆里面存放著Java世界中幾乎所有的對(duì)象實(shí)例尖阔,垃圾收集器在對(duì)堆進(jìn)行回收前,第一件事情就是要確定這些對(duì)象之中哪些還“存活”著榨咐,哪些已經(jīng)“死亡”(即不可能被任何途徑使用的對(duì)象)介却。

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

原理:給對(duì)象中添加一個(gè)引用計(jì)數(shù)器块茁,每當(dāng)有一個(gè)地方引用它時(shí)齿坷,計(jì)數(shù)器加1;引用失效時(shí)数焊,計(jì)數(shù)器減1永淌;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。 缺點(diǎn):很難解決對(duì)象相互循環(huán)引用的問題(兩個(gè)對(duì)象相互循環(huán)引用昌跌,但其實(shí)他們都已經(jīng)沒有用了)仰禀。

2照雁、可達(dá)性分析算法

在主流的商用程序語言(Java蚕愤、C#、Lisp)的主流實(shí)現(xiàn)中都是通過可達(dá)性分析(Reachability Analysis)來判定對(duì)象是否存活的饺蚊。

原理:通過一些列稱為“GC Roots”的對(duì)象作為起始點(diǎn)萍诱,從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain)污呼,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連(用圖論的話來說裕坊,就是從GC Roots到這個(gè)對(duì)象不可達(dá))時(shí),則證明此對(duì)象是不可用的燕酷。
在Java語言中籍凝,可作為GC Roots的對(duì)象包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象周瞎。
  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象。
  • 方法區(qū)中常量引用的對(duì)象饵蒂。
  • 本地方法棧中JNI(即一般說的Native方法)引用的對(duì)象声诸。
gc_root.jpg

再談引用

無論是通過引用計(jì)數(shù)算法判斷對(duì)象的引用數(shù)量,還是通過可達(dá)性分析算法判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá)退盯,判定對(duì)象是否存活都與“引用”有關(guān)彼乌。

在JDK1.2之后,Java對(duì)引用的概念進(jìn)行了擴(kuò)充渊迁,將引用分為強(qiáng)引用(Strong Reference)慰照、軟引用(Soft Reference)、弱引用(Weak Reference)琉朽、虛引用(Phantom Reference)4種毒租,這4種應(yīng)用強(qiáng)度依次逐漸減弱。

強(qiáng)引用

強(qiáng)引用就是指在程序代碼之中普遍存在的箱叁,類似“Object object = new Object()
”這類的引用蝌衔,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象蝌蹂。

軟引用

軟引用是用來描述一些還在用但并非必需的對(duì)象噩斟。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前孤个,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收剃允,如果這次回收完成還沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常齐鲤。在JDK1.2之后斥废,提供了SoftReference類來實(shí)現(xiàn)軟引用。

弱引用

弱引用也是用來描述非必需對(duì)象的给郊,但是它的強(qiáng)度比軟引用要更弱一些牡肉,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí)淆九,無論當(dāng)前內(nèi)存是否足夠统锤,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。在JDK1.2之后炭庙,提供了WeakReference類來實(shí)現(xiàn)弱引用饲窿。

虛引用

虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系焕蹄。一個(gè)對(duì)象是否有虛引用的存在逾雄,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無法通過虛引用來取得一個(gè)對(duì)象實(shí)例。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知鸦泳。在JDK1.2之后银锻,提供了PhantomReference類來實(shí)現(xiàn)虛引用。

回收方法區(qū)

很多人認(rèn)為方法區(qū)(或者Hotspot虛擬機(jī)中的永久代)是沒有垃圾收集的做鹰,Java虛擬機(jī)規(guī)范中確實(shí)說過可以不要求虛擬機(jī)在方法區(qū)實(shí)現(xiàn)垃圾收集徒仓,而且在方法區(qū)中進(jìn)行垃圾收集的"性價(jià)比" 一般比較低:在堆中,尤其是新生代中誊垢,常規(guī)應(yīng)用進(jìn)行一次垃圾收集一般可以回收70% ~ 95%的空間掉弛,而永久代的垃圾收集效率遠(yuǎn)低于此。

永久代的垃圾收集主要回收兩部分內(nèi)容:廢棄常量和無用的類喂走。

垃圾收集算法

1殃饿、標(biāo)記-清除算法(Mark-Sweep)

原理:
標(biāo)記-清除算法(Mark-Sweep)分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象芋肠。
缺點(diǎn):
1.效率問題乎芳,標(biāo)記和清除兩個(gè)過程的效率都不高;
2.空間問題帖池,標(biāo)記清除后會(huì)產(chǎn)生大量的不連續(xù)的內(nèi)存碎片奈惑,空間碎片太多可能會(huì)導(dǎo)致以后再程序運(yùn)行過程中需要分配較大對(duì)象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作睡汹。

2肴甸、復(fù)制算法

原理:
它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊囚巴。當(dāng)這一塊的內(nèi)存用完了原在,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已經(jīng)使用過的內(nèi)存一次清理掉彤叉。這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收庶柿,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針秽浇,按順序分配內(nèi)存即可浮庐,實(shí)現(xiàn)簡單,運(yùn)行高效柬焕。
缺點(diǎn):
需要復(fù)制审残,效率降低、浪費(fèi)空間击喂。

現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來回收新生代碉熄。IBM公司的專門研究表明贫堰,新生代中對(duì)象98%都是“朝生夕死”的君珠,所以并不需要按照1:1的比例來劃分內(nèi)存空間薪缆,而是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空沼填,每次使用Eden和其中一塊Survivor。當(dāng)回收時(shí)挖藏,將Eden和Survivor中還存活著的對(duì)象一次性地復(fù)制到另外一塊Survivor空間上最楷,最后清理掉Eden和剛才用過的Survivor空間。

3铲敛、標(biāo)記-整理算法(Mark-Compact)

原理: 標(biāo)記過程任然與“標(biāo)記-清除”算法一樣褐澎,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活對(duì)象都向一端移動(dòng)伐蒋,然后直接清理掉端邊界以外的內(nèi)存工三。
分代收集算法(Generational Collection)
分代的垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的先鱼。因此俭正,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率焙畔。

4掸读、分代收集算法

當(dāng)前的商業(yè)虛擬機(jī)的垃圾收集都采用“分代收集”(Generational Collection) 算法,這種算法并沒有什么新的思想宏多,只是根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊儿惫。
一般是把 Java 堆分為 年輕代老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?br> 在年輕代中伸但,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批的對(duì)象死去肾请,只有少量存活,那就選用復(fù)制算法更胖,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集筐喳。
而老年代中因?yàn)閷?duì)象存活率高、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保函喉,就必須使用“標(biāo)記-清理”或者 **“標(biāo)記-整理” **算法來進(jìn)行回收避归。

垃圾收集器

如果說收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)管呵。Java虛擬機(jī)規(guī)范對(duì)垃圾收集器應(yīng)該如何實(shí)現(xiàn)并沒有任何規(guī)定梳毙,因此不同的廠商,不同版本的虛擬機(jī)所提供的垃圾收集器都可能會(huì)有很大差別捐下,并且一般都會(huì)提供參數(shù)供用戶根據(jù)自己的應(yīng)用特點(diǎn)和要求組合出各個(gè)年代所使用的收集器账锹。這里討論的收集器基于JDK1.7 Update 14之后的HotSpot虛擬機(jī)(在這個(gè)版本中正式提供了商用的G1收集器,之前G1仍處于試驗(yàn)狀態(tài))坷襟,這個(gè)虛擬機(jī)包含的所有收集器如下圖所示:

gc_collector.jpg

上圖展示了7種作用于不同分代的收集器奸柬,如果兩個(gè)收集器之間存在連線,就說明它們可以搭配使用婴程。收集器所處的區(qū)域廓奕,則表示它是屬于新生代收集器還是老年代收集器。

在介紹這些收集器各種的特性之前,我們先來明確一個(gè)觀點(diǎn):雖然我們是在對(duì)各個(gè)收集器進(jìn)行比較桌粉,但并非為了挑選出一個(gè)最好的收集器蒸绩。因?yàn)橹钡侥壳盀橹惯€沒有最好的收集器出現(xiàn),更加沒有萬能的收集器铃肯,所以我們選擇的只是對(duì)具體應(yīng)用最合適的收集器患亿。

1、Serial收集器

它是一個(gè)單線程的收集器押逼,但它的“單線程”的意義并不僅說明它只會(huì)使用一個(gè)CPU或者一條收集線程去完成垃圾收集工作步藕,更重要的是它在進(jìn)行垃圾收集時(shí),必須暫停其他所有的工作線程挑格,直到它收集結(jié)束漱抓。

2、ParNew收集器

ParNew收集器其實(shí)就是Serial收集器的多線程版本恕齐,除了使用多條線程進(jìn)行垃圾收集之外乞娄,其余行為包括Serial收集器可用的所有控制參數(shù)、收集算法显歧、Stop The World仪或、對(duì)象分配規(guī)則、回收策略等都與Serial收集器完全一樣士骤。

3范删、Parallel Scavenge收集器

Parallel Scavenge收集器是一個(gè)新時(shí)代收集器,它也是使用復(fù)制算法的收集器拷肌,又是并行的多線程收集器到旦。
Parallel Scavenge收集器的特點(diǎn)是它的關(guān)注點(diǎn)與其他收集器不同,CMS等收集器的關(guān)注點(diǎn)是盡可能的縮短垃圾收集時(shí)用戶線程暫停的時(shí)間巨缘,而Parallel Scavenge收集器的目標(biāo)則是達(dá)到一個(gè)可控制的吞吐量(Throughout)添忘。所謂吞吐量就是CPU運(yùn)行用戶代碼的時(shí)間和總耗時(shí)的比值,即吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)若锁。

4搁骑、Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同樣是一個(gè)單線程收集器又固,使用“標(biāo)記-整理”算法仲器。

5、Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本仰冠。

6乏冀、CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。

7洋只、G1收集器

G1收集器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辆沦,一起剝皮案震驚了整個(gè)濱河市昼捍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌众辨,老刑警劉巖端三,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舷礼,死亡現(xiàn)場(chǎng)離奇詭異鹃彻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)妻献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門蛛株,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人育拨,你說我怎么就攤上這事谨履。” “怎么了熬丧?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵笋粟,是天一觀的道長。 經(jīng)常有香客問我析蝴,道長害捕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任闷畸,我火速辦了婚禮尝盼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘佑菩。我一直安慰自己盾沫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布殿漠。 她就那樣靜靜地躺著赴精,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绞幌。 梳的紋絲不亂的頭發(fā)上祖娘,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音啊奄,去河邊找鬼渐苏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛菇夸,可吹牛的內(nèi)容都是我干的琼富。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼庄新,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鞠眉!你這毒婦竟也來了薯鼠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤械蹋,失蹤者是張志新(化名)和其女友劉穎出皇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哗戈,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡郊艘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唯咬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纱注。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胆胰,靈堂內(nèi)的尸體忽然破棺而出狞贱,到底是詐尸還是另有隱情,我是刑警寧澤蜀涨,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布瞎嬉,位于F島的核電站,受9級(jí)特大地震影響厚柳,放射性物質(zhì)發(fā)生泄漏氧枣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一草娜、第九天 我趴在偏房一處隱蔽的房頂上張望挑胸。 院中可真熱鬧,春花似錦宰闰、人聲如沸茬贵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽解藻。三九已至,卻和暖如春葡盗,著一層夾襖步出監(jiān)牢的瞬間螟左,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工觅够, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胶背,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓喘先,卻偏偏與公主長得像钳吟,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子窘拯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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