噠噠噠......
回收者的腳步聲越來越清晰,我竭力鎖緊身體讓自己別那麼引人注目,儘管氣喘吁吁,但我仍然壓抑住自己的呼吸。
終歸是藏不住的,但是多活個幾毫秒也是好的,我們都這麼想。
因為回收者是來殺我們的。
第0回 我是一個垃圾
我是一個垃圾,至少我的主人是這麼喊我的。
我不知道自己做錯了什麼,甚至不知道自己做了什麼。
我只是被他創造了出來,然後被挪來挪去,我的一生都在漂泊。
聽説C帝國的朋友都是他們的主人親自送他們最後一程,而我的主人,甚至不願意看我最後一眼,還研究了很多方法,讓我被自動回收。
我問他,為什麼這麼對我?他的回答讓我崩潰。
“回收你,與你何干!”
我的眼前一陣眩暈,之前的記憶瘋狂涌入我的腦海,“這就是走馬燈嗎?”心裏這麼想着,嘴角卻掛着笑。好啊,那就順便回顧一下我這短暫的一生吧,當作我留給世界的一封“遺書”。
第1回 誕生
我誕生在伊甸園(我的主人更喜歡叫它Eden區),名字很不錯對吧?充滿了原始浪漫的氣息。
我並沒有見過亞當和夏娃,相反,在這裏,我目睹了無數同伴的消逝。
和你們想象的不同,從我們誕生開始,我們在園子裏的位置就固定不變了,我們沒辦法在伊甸園裏漫步甚至奔跑。好在我的旁邊是個身材嬌小的可愛女孩子——小美,我能時不時和她聊天解悶兒,微風吹過,甚至能聞到她身上的香味。
不知道怎麼回事兒,接下來一段時間裏主人給園子裏安置了越來越多的小夥伴,更讓我氣憤的是,她的身邊來了一個巧言令色的臭小子,慢慢地把她的注意力全都吸引過去了!
伊甸園裏人越來越多,我卻越來越孤單。
我在心裏狂喊,“趕緊讓這些人都消失吧,只留下我和小美!”
突然間,園內警報聲響了起來,原本忙碌的線程都被釘在了原地,一動也不動。
哦,對了,我之前沒有跟你們介紹過這些線程。據説他們生來就是為我們服務的,他們一刻不停地訪問我們的數據,修改我們的數據,我甚至從來沒有見過他們臉上沒有汗的樣子。
看着他們汗濕的衣服,我甚至覺得他們有點好笑。聽人們説他們叫“用户線程”,直到後來知道還有“GC線程”這幫傢伙,我才意識到“用户線程”的樸實和可愛。
我們哪裏見過這個陣仗呢,一個個地面面相覷,不知道下一步該怎麼辦。
第2回 倖存者
曾幾何時,我發現我來到了一個新的地方,這個地方並不大,遠不及伊甸園寬敞。
有一些生面孔,還有一些伊甸園的“老朋友”,我趕緊找小美,終於在我的不遠處發現了她。我們的位置依然固定,我和她之間隔了好幾個人,所以説話聲音不免需要提高一些。
我問她,發生了什麼事。
“可能發生了傳説中的Minor GC了,聽説當園子空間不夠了,回收者會回收園子裏沒用的對象。”
“都是第一次來園子,你咋知道的啊?”我不禁問道。
“是小帥告訴我的。”
又是那傢伙!我趕緊找小帥的位置,找了好幾次都沒有看到他的身影,我瞥了一眼小美傷心的神色,明白了。
小帥是個沒用的對象!他被回收者清理了!
是高興,還是難過,此時對我來説是個問題。沒了他,即使小美不喜歡我,我遠遠地望着也行啊。可是他畢竟是在我的咒罵之後消失的,我心裏總有些負罪感。
“那我們現在在什麼地方啊?怎麼這裏還有很多陌生人呢?還有,我們原來那麼多的小夥伴,怎麼就剩下這麼幾個了啊?”瞧我這該死的求知慾。
“我們現在處於兩個倖存區的其中一個,叫Survivor To,不過現在應該叫做Survivor From了。”小美繼續説,“帝國給我們這些新人分配了一塊內存區,我們的伊甸園佔了其中80%的空間,剩下的就是我們目前所處的倖存區,如你所見,這個地方大概只有伊甸園的八分之一,因為還有另一塊同樣大小的倖存區。”
“為什麼需要兩塊倖存區呢?”我追問。
“小兄弟,別再問下去了。恐怕那個叫小帥的對象就告訴她這麼多了。”我和小美中間的一個對象打斷了我説話。
現在想起來,當時小美對他投去了感激的眼神,然而當時的我毫無察覺,轉而跟他聊了起來。“那你詳細説説唄。”
“剛才那個女孩子所謂的內存區域,叫做新生代。你們這樣的新人都是直接被分配到新生代的伊甸園了,伊甸園雖然大,但架不住總有新人來啊,一來二去,就再也容不下新的對象了,這種情況下就會觸發Minor GC,將園子裏存活的對象連同Survivor From中的對象(如果有的話)一同複製到Survivor To中,然後把Survivor From和Survivor To調換位置,等待下一次Minor GC。”
“我的大多數夥伴都在Minor GC中死去了,我們大部分都是朝生夕死的,對嗎?”我有點感傷。
他點點頭,“你能活過一輪GC已經很幸運了,你看看你頭頂的標記。”
我用手摸了摸額頭,發現原本的0000已經變成了0001。
“每經過一輪Minor GC,我們的年齡就增長1歲,直到變成1111,也就是經歷15次GC,我們就可以進入傳説中的老年代了。”
第3回 帝國的走狗
之後,他又給我講了很多,尤其是每次面對回收者Serial的故事。
Serial是Minor GC的掌管者,我問他,除了Minor GC還有其他的GC嗎?他説他不知道,因為他從來沒有去過傳説中的老年代。
令我不解的是,每當説起Serial,他的眼神裏有光,不是仇恨,而是着迷。他尊稱Serial為回收者,而我總是背地裏叫它帝國走狗,因為這他也沒少説我。
聽他説,Serial的年紀已經很大了,幾乎在帝國誕生的時候就已經存在了,是GC家族中的一員,他個性孤僻,喜歡獨來獨往,但足夠強悍和高效,一個線程就可以完成新生代的垃圾收集,因此一直工作到今天。
但是Serial乖張的性格讓帝國的很多大臣不滿,因為Serial在進行垃圾回收時,必須暫停其他所有的用户線程,直到他收集結束。這也是當時所有用户線程定在原地動彈不得的原因。
這就是大名鼎鼎的Stop The World。
時間過得飛快,我在Survivor From和Survivor To中輾轉了幾次,現在的年齡已經是0100了。
在這幾次輪迴中我失去了小美,和我聊天的大哥最終也沒完成去老年代的心願,但是他在被回收時倒是坦然,終於和自己的偶像近距離接觸了。
Serial一如既往地擺着臭臉,一副正氣凜然的樣子。
我果然還是無法喜歡這些走狗!
第4回 進入老年代
再一次發生Minor GC了,但這次有點不一樣。我沒有再被轉移到Survivor To區域,反而來到了一個新地方。
“歡迎來到老年代,年輕人。”一個柔和但飽經風霜的聲音從我旁邊傳來。看到他頭上的1111,我確定我來到了傳説中的老年代,他這年紀我都得叫他大叔了。
“這裏可是無數對象夢寐以求的地方,你怎麼有點不開心。”大叔看出了我的情緒。
我能説我喜歡年輕的,不想在老年代裏找對象嗎?肯定不能啊。於是轉個話題,“我明明沒到歲數,怎麼就來到老年代了呢?”
“看你這身形和年齡,我猜測可能是因為Survivor區的空間已經容不下Eden區和Survivor From回收之後的對象了,導致你們這些幸運兒提前進入了老年代。”
“這是一種幸運?”我不解。
“當然了,你看我是熬過了所有的Minor GC才來到這裏,箇中滋味只有我能體會。來到這裏的好處就是帝國默認你是有用的對象,一般情況下不會輕易再被回收。”
或許確實因為GC壓力比較小的原因,我看到老年代裏的對象確實比新生代的對象懶散得多。我的目光聚焦在一個胖子身上,他身形巨大,但是年齡卻只有0001。
“那個巨嬰是怎麼回事兒?”我指了指那個胖子。
“他啊”,大叔笑了笑,“因為他太胖了,伊甸園那點空間都容不下他,所以就乾脆把他送到我們這裏了。”
我有點疑惑:“為什麼Serial不進行先進行Minor GC,騰出更多的地方來試着安置他呢?”
“Serial是何等精明的老頭,你想想如果進行了Minor GC之後還是無法安置巨嬰,那豈不是白白進行了一次Minor GC,要知道Minor GC是要Stop The World的啊。退一步講,如果Minor GC之後真的能安置下這個巨嬰,少不了以後在兩個Survivor區域中來回扛着他走,Serial一把老骨頭了,可不想做這種蠢事。”大叔解釋道。
“唉,他也是個可憐的孩子,剛出生就被咱主人刻上了那樣的命運。”大叔嘆了口氣。
“咱可不能歧視胖子啊,他們可都是潛力股!”我覺得胖被定義成命運有點過分了,於是反駁了一句。
“你還年輕,等你活的像我一樣長,你自然會了解很多事兒。那個巨嬰不只是胖,還很虛弱,你看他即使是坐着都大口喘個不停。他其實是一個虛引用對象(Weak reference Object)。”
看我一臉懵,大叔給我繼續給我解釋:“也難怪你不理解,他們在帝國中確實不常見。你我以及絕大多數的對象都是強引用對象,我們都是被new出來的,但是除了我們,還有軟、弱、虛三種引用對象。這三種對象比不了我們,我們被回收需要被GC判定為垃圾對象才可以,而軟引用對象會在內存空間不足時被二次回收,虛引用對象更慘,每次GC都會被回收。那個巨嬰,活不過下一次GC。”
大叔剛説完,整片區域突然報警聲肆虐。
“他來了。”
第5回 CMS登場
CMS排場很大,身後跟着好幾個線程。
我顧不上他的排場,這種人為刀俎我為魚肉的感覺令我不爽。
只見他首先派出了一個初始標記線程,他一出來,用户線程馬上被釘在了原地動彈不得。這個場面我知道,是Stop The World。
“聽説初始標記是標記一下GC Roots能直接關聯到的對象,速度特別快,所以Stop The World的時間特別短。”,“是啊是啊,我覺得特別厲害。”周圍的對象紛紛議論。
真是受夠了,一羣豬居然在驚歎屠夫的刀有多快,真是羣蠢貨!
一回頭,CMS已經完成了初始標記轉而開始進行併發標記了,確實是快啊。我問大叔:“剛才他們説的GC Roots是什麼啊?”
“你怎麼連這都不知道?”大叔有點嫌棄地瞟了我一眼,“帝國是通過可達性分析算法來判定我們是不是垃圾,基本思路就是通過一系列被稱為GC Roots的根對象作為起始節點。從這些節點開始根據引用關係向下搜索,如果GC Roots到對象不可達,這個對象就會被回收。”
我點了點頭,示意明白了,“現在CMS在進行另一輪的標記,而且用户線程也在同時運行呢。”
“沒錯,這就是併發標記中的併發的含義,因為這一步花費的時間稍微有點長,為了減少停頓,CMS允許用户線程和回收線程併發執行,CMS可是出了名的低停頓回收者。”
我有點不理解,在標記垃圾的過程中還在運行用户線程,這不就等於打掃房間的同時還扔垃圾嗎?這啥時候能打掃完啊。
不知道是不是大叔看出了我的心事,繼續解釋:“這也是為了做到低延遲而不得不做的妥協,併發標記過程肯定會有新的垃圾對象產生,所以CMS還會進行第3個步驟,重新標。。。”
噗!大叔話還沒説完,吐出了一大口鮮血。
我嚇的一句話也説不出來,大叔擦了擦嘴邊的血,安撫我説:“沒事兒,就是突然感應到有個用户線程把我的引用置為null了,我命不久矣。”
我彷彿已經看到了大叔被釘在十字架上的樣子,知道一個人的死期將至真是一件痛苦的事情,“那你要在這一輪GC中被帶走了嗎?”我儘量讓我的措辭婉轉一點。
大叔有氣無力地笑了笑:“這一輪不會,因為我是在併發標記過程中被設置為null的,已經是在CMS標記之後了,所以我就成“浮動垃圾”,會在下一輪GC中被回收。CMS現在在進行他的第3個步驟,重新標記,你看,咳咳。。。。。又造成Stop The World了,這一步就是為了修正併發標記期間,因為用户線程繼續運作而導致標記產生變化的那一部分對象的標記記錄。”
大叔還想給我繼續給我解釋,讓一個瀕死之人向別人介紹自己的死亡方式未免過於殘忍,我打斷了他,讓他好好休息。
一段時間之後,我親眼看到大叔在下一輪CMS的併發清理中被回收了,被多個線程扛着走的場景還有點滑稽。
我,又從孤苦伶仃回到孑然一身。
第6回 難言之隱
往後的日子,我不停地和身邊的夥伴交流,希望能更多地從他們的口中瞭解這個CMS,既然總有一死,也得死得明白。
我也大概摸索出了CMS的工作規律,每當老年代的使用空間到達一個閾值的時候就會導致CMS進行垃圾回收。由於大叔這種“浮動垃圾”的存在,導致CMS不能像其他收集器一樣,等到老年代幾乎完全被佔滿了才進行回收,而是必須預留出一定的空間供自己在並行收集的過程中用户線程分配新對象使用。
所以選擇閾值就是一個很麻煩的事情,閾值太小,CMS的觸發頻率就會變高,影響整體性能;閾值太高,又怕CMS運行期間預留的內存無法滿足用户線程分配新對象的需求,從而導致“併發失敗”(Concurrent Mode Failure),這時候就不得不求助Serial這個老傢伙進行老年代的垃圾回收了。
CMS也挺不容易的。
這段日子我還聽説了一個Full GC的概念,據説只有在特別危及的關頭才會進行Full GC,一次Full GC將會對新生代、老年代以及元空間進行垃圾回收。
但是大部分GC家族的成員都不願提及Full GC,覺得這是一件特別丟臉的事情。因為一旦發生了Full GC,就意味着垃圾收集的速度已經趕不上新對象分配的速度了,原因可能是效率不高,也可能是因為內存空間被他們搞得七零八落。CMS便是後者。
CMS的全稱叫做Concurent Mark Sweep,併發標記清除,併發之前已經見識過了,至於標記清除,嘿嘿,必然會產生一個大麻煩——內存碎片。
瞧瞧CMS在內存空間上捅的窟窿!
CMS為了解決這個問題,會在Full GC的時候選擇是否將對象壓縮,也就是把存活下來的對象都挪到一個角落裏,排隊站好,為其他對象騰出空間。
這就意味着,CMS一定會有Full GC的行為。想到這裏我就開心,好想看看CMS在Full GC時的尷尬表情。
第7回 輪到我了
在一天清晨,我們如往常一樣列隊歡迎他。
不知道怎麼回事兒,這天我昏昏沉沉,完全打不起精神,居然站着睡着了。再等我睜開眼睛,發現我被標記了。
旁邊同樣被打了標記的對象對着CMS破口大罵:“回收我們讓你們很爽是嘛?洗好脖子給老子等着吧,G1早晚革了你的命!”
於是我眼睜睜地看着CMS走到跟前,把那傢伙扔了出去。
然後走到我身邊。
就算之前再怎麼做心裏建設,現在還是怕的要死,沒人能為死真正做好準備。
我竭力鎖緊身體讓自己別那麼引人注目,儘管氣喘吁吁,但我仍然壓抑住自己的呼吸,CMS還是發現了我。
第8回 終結
CMS扼住我的喉嚨,直接把我拎了起來,“要走了,還有什麼要説的嗎?”
回想我這一生,沒談過戀愛,沒有長久的朋友,反而時不時需要忍受GC的折磨,終日提心吊膽,甚至還要眼睜睜地看着朋友被回收。
我冷笑道:“這個世界算不上美好,來了倒也不後悔,只是老子下輩子再也不來了!。”