15-大廠面試題-JVM垃圾回收采用的是什么算法,有什么區(qū)別和優(yōu)劣止吁?

通過之前的學(xué)習(xí)被辑,我們知道了JVM會通過可達(dá)性算法來篩選出哪些對象是可回收的,哪些對象是不可回收的敬惦,GCRoots對象是哪些盼理,java的引用類型有哪些以及finlize()方法的作用。同時我們也知道了當(dāng)一個對象在創(chuàng)建的時候是存放在堆內(nèi)存中的新生代里的俄删,那么當(dāng)新生代內(nèi)存滿了后就會觸發(fā)Minor GC宏怔;但是問題是我們?nèi)绾吾槍π律鷥?nèi)存進(jìn)行管理,以及如何進(jìn)行回收這也是一個值得分析和探討的問題畴椰。

這里針對新生代的垃圾回收算法臊诊,叫做復(fù)制算法

3.1復(fù)制算法

我們先來回顧下之前講堆內(nèi)存的結(jié)構(gòu)分配

存儲在JVM中的Java對象可以被劃分為兩類:

? 一類是生命周期較短的瞬時對象,這類對象的創(chuàng)建和消亡都非常迅速斜脂,生命周期短的抓艳,及時回收即可。

? 另外一類對象的生命周期卻非常長帚戳,在某些極端的情況下還能夠與JVM的生命周期保持一致玷或。

Java堆區(qū)進(jìn)一步細(xì)分的話,可以劃分為年輕代(YoungGen)和老年代(oldGen)片任,其中年輕代又可以劃分為Eden空間偏友、Survivor0空間和Survivor1空間(有時也叫做from區(qū)、to區(qū))对供。

image

這里大家需要去思考位他,為什么JVM會分成年輕代和老年代,以及年輕代里面又為什么要再劃分出三個區(qū)域产场,這樣做的好處是什么鹅髓?

我們先來分析新生代(年輕代)的復(fù)制算法以及所帶來的的優(yōu)劣

1969年Fenichel提出了一種稱為“半?yún)^(qū)復(fù)制”(Semispace Copying) 的垃圾收集算法, 它將可用內(nèi)存按容量劃分為大小相等的兩塊京景, 每次只使用其中的一塊迈勋。當(dāng)這一塊的內(nèi)存用完了, 就將還存活著的對象復(fù)制到另外一塊上面醋粟, 然后再把已使用過的內(nèi)存空間一次清理掉靡菇。

簡單點來說,就是把新生代的內(nèi)存分為兩塊米愿,如下圖所示:

image

這時比如我們的代碼如下:

<pre data-tool="mdnice編輯器" style="margin: 10px 0px; padding: 0px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">public class Test { public static void main(String[] args) { registUser(); } public static void registUser(){ User user = new User(); } } </pre>

那么對應(yīng)內(nèi)存中的分配就如下:

image

那么如果我們假設(shè)我們的程序不停止厦凤,依然在運(yùn)行,這時不停的調(diào)用registUser()方法生產(chǎn)大量的User對象育苟,對應(yīng)棧幀已經(jīng)退出较鼓,沒有指向?qū)?yīng)的對象,那么就會在堆內(nèi)存中產(chǎn)生大量的垃圾對象:

image

當(dāng)新生代第一塊區(qū)域內(nèi)容已滿违柏,裝不下的時候博烂,就會觸發(fā)Minor GC回收垃圾。

這時漱竖,如果我們僅僅是采用標(biāo)記算法禽篱,標(biāo)記哪些對象是可回收的,哪些對象是不可回收的馍惹,然后針對可回收的內(nèi)容進(jìn)行回收躺率,那么會導(dǎo)致一個不好的后果,就是產(chǎn)生大量的內(nèi)存碎片万矾。

內(nèi)存碎片一般是由于空閑的連續(xù)空間比要申請的空間小悼吱,導(dǎo)致這些小內(nèi)存塊不能被利用。產(chǎn)生內(nèi)存碎片的方法很簡單良狈,舉個例:假設(shè)有一塊一共有100個單位的連續(xù)空閑內(nèi)存空間后添,范圍是099。如果你從中申請一塊內(nèi)存薪丁,如10個單位遇西,那么申請出來的內(nèi)存塊就為09區(qū)間。這時候你繼續(xù)申請一塊內(nèi)存窥突,比如說5個單位大努溃,第二塊得到的內(nèi)存塊就應(yīng)該為1014區(qū)間。如果你把第一塊內(nèi)存塊釋放阻问,然后再申請一塊大于10個單位的內(nèi)存塊梧税,比如說20個單位。因為剛被釋放的內(nèi)存塊不能滿足新的請求称近,所以只能從15開始分配出20個單位的內(nèi)存塊〉诙樱現(xiàn)在整個內(nèi)存空間的狀態(tài)是09空閑,1014被占用刨秆,1524被占用凳谦,2599空閑。其中09就是一個內(nèi)存碎片了衡未。如果1014一直被占用尸执,而以后申請的空間都大于10個單位家凯,那么09就永遠(yuǎn)用不上了,造成內(nèi)存浪費(fèi)如失。如果你每次申請內(nèi)存的大小绊诲,都比前一次釋放的內(nèi)村大小要小,那么就申請就總能成功褪贵。

image

如果內(nèi)存碎片過多掂之,就會造成大量的內(nèi)存浪費(fèi),隨著回收的次數(shù)越多脆丁,這樣的碎片可能更多更雜亂世舰,因此這樣直接針對一塊內(nèi)容空間回收的做法是不可取的。

因此JVM采用了復(fù)制算法槽卫,我們圖中有一塊一直未使用的空間可以派上用場了跟压。當(dāng)真正發(fā)生垃圾回收的時候,JVM會將第一塊空間中哪些對象是可回收的晒夹,不能回收的進(jìn)行標(biāo)記裆馒,然后將不可回收的對象統(tǒng)統(tǒng)復(fù)制到下面那塊區(qū)域中,并且復(fù)制的時候可以緊湊的排列在一起丐怯,最大化利用內(nèi)存空間:

image

那么我們可以直接一次性回收掉上面空間的所有垃圾對象喷好,同時有新的對象產(chǎn)生的時候,直接放在下面這塊區(qū)域進(jìn)行存儲即可读跷。 那么這時上面空間就會騰出梗搅,下面空間就月會越來越多:

image

當(dāng)下面區(qū)域裝滿的時候,同樣按照剛才的邏輯復(fù)制存活對象到上面區(qū)域效览,一次性回收下面區(qū)域內(nèi)存无切。兩塊區(qū)域內(nèi)存就可以一直重復(fù)循環(huán)使用。

復(fù)制算法的缺點

那么復(fù)制算法確實可以解決內(nèi)存碎片的問題丐枉,也使得我們的回收工作更加效率哆键,不過其缺點也是顯而易見的。這種復(fù)制回收算法的代價是將可用內(nèi)存縮小為了原來的一半瘦锹, 空間浪費(fèi)未免太多了一點 籍嘹。

如果我們給新生代內(nèi)存分配一個G的大小,那么兩塊區(qū)域平均分配弯院,各自占512MB內(nèi)存辱士,從始至終就只有一半的內(nèi)存可用,這樣的算法對內(nèi)存的使用效率就太低了听绳!

現(xiàn)在的商用Java虛擬機(jī)大多都優(yōu)先采用了這種收集算法去回收新生代颂碘, IBM公司曾有一項專門研究對新生代“朝生夕滅”的特點做了更量化的詮釋——新生代中的對象有98%熬不過第一輪收集。因此并不需要按照1∶ 1的比例來劃分新生代的內(nèi)存空間椅挣。

在1989年头岔, Andrew Appel針對具備“朝生夕滅”特點的對象塔拳, 提出了一種更優(yōu)化的半?yún)^(qū)復(fù)制分代策略, 現(xiàn)在稱為“Appel式回收”切油。HotSpot虛擬機(jī)的Serial蝙斜、 ParNew等新生代收集器均采用了這種策略來設(shè)計新生代的內(nèi)存布局。Appel式回收的具體做法是把新生代分為一塊較大的Eden空間和兩塊較小的Survivor空間澎胡, 每次分配內(nèi)存只使用Eden和其中一塊Survivor。發(fā)生垃圾搜集時娩鹉, 將Eden和Survivor中仍然存活的對象一次性復(fù)制到另外一塊Survivor空間上攻谁, 然后直接清理掉Eden和已用過的那塊Survivor空間。HotSpot虛擬機(jī)默認(rèn)Eden和Survivor的大小比例是8∶ 1弯予, 也即每次新生代中可用內(nèi)存空間為整個新生代容量的90%(Eden的80%加上一個Survivor的10%) 戚宦, 只有一個Survivor空間, 即10%的新生代是會被“浪費(fèi)”的锈嫩。當(dāng)然受楼, 98%的對象可被回收僅僅是“普通場景”下測得的數(shù)據(jù), 任何人都沒有辦法百分百保證每次回收都只有不多于10%的對象存活呼寸, 因此Appel式回收還有一個充當(dāng)罕見情況的“逃生門”的安全設(shè)計艳汽, 當(dāng)Survivor空間不足以容納一次Minor GC之后存活的對象時, 就需要依賴其他內(nèi)存區(qū)域(實際上大多就是老年代) 進(jìn)行分配擔(dān)保(Handle Promotion) 对雪。

image

內(nèi)存的分配擔(dān)保好比我們?nèi)ャy行借款河狐, 如果我們信譽(yù)很好, 在98%的情況下都能按時償還瑟捣, 于是銀行可能會默認(rèn)我們下一次也能按時按量地償還貸款馋艺, 只需要有一個擔(dān)保人能保證如果我不能還款時, 可以從他的賬戶扣錢迈套, 那銀行就認(rèn)為沒有什么風(fēng)險了捐祠。內(nèi)存的分配擔(dān)保也一樣, 如果另外一塊Survivor空間沒有足夠空間存放上一次新生代收集下來的存活對象桑李, 這些對象便將通過分配擔(dān)保機(jī)制直接進(jìn)入老年代踱蛀, 這對虛擬機(jī)來說就是安全的。

小結(jié)

本章節(jié)我們介紹了JVM垃圾回收的算法-標(biāo)記復(fù)制算法芙扎,以及復(fù)制算法的缺點星岗。下一節(jié)我們將繼續(xù)介紹JVM內(nèi)存的分配以及回收策略,比如:對象優(yōu)先在Eden分配戒洼,大對象直接進(jìn)入老年代俏橘,以及長期存活的對象將進(jìn)入老年代,動態(tài)對象的年齡判斷以及空間分配擔(dān)保原則圈浇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寥掐,一起剝皮案震驚了整個濱河市靴寂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌召耘,老刑警劉巖百炬,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異污它,居然都是意外死亡剖踊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門衫贬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來德澈,“玉大人,你說我怎么就攤上這事固惯“鹪欤” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵葬毫,是天一觀的道長镇辉。 經(jīng)常有香客問我,道長贴捡,這世上最難降的妖魔是什么忽肛? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮栈暇,結(jié)果婚禮上麻裁,老公的妹妹穿的比我還像新娘。我一直安慰自己源祈,他們只是感情好煎源,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著香缺,像睡著了一般手销。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上图张,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天锋拖,我揣著相機(jī)與錄音,去河邊找鬼祸轮。 笑死兽埃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的适袜。 我是一名探鬼主播柄错,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了售貌?” 一聲冷哼從身側(cè)響起给猾,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颂跨,沒想到半個月后敢伸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡恒削,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年池颈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钓丰。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡饶辙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斑粱,到底是詐尸還是另有隱情,我是刑警寧澤脯爪,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布则北,位于F島的核電站,受9級特大地震影響痕慢,放射性物質(zhì)發(fā)生泄漏尚揣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一掖举、第九天 我趴在偏房一處隱蔽的房頂上張望快骗。 院中可真熱鬧,春花似錦塔次、人聲如沸方篮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藕溅。三九已至,卻和暖如春继榆,著一層夾襖步出監(jiān)牢的瞬間巾表,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工略吨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留集币,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓翠忠,卻偏偏與公主長得像鞠苟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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