小垃圾修真?zhèn)鳎↗AVA垃圾回收機制GC)

關(guān)鍵問題:伊甸園和幸存區(qū)為什么都屬于年輕代,而不是分開來算?幸存區(qū)為什么有兩個?什么時候觸發(fā)垃圾清理? 怎么樣知道對象不在被引用?

伊甸園Edem

你是一個在伊甸園生活的小動物,每天都有新的小動物被上帝放進來,有一天有一只大象要進來,但是伊甸園實在裝不下了,上帝就會發(fā)動大洪水,把那些已經(jīng)完成使命的小動物沖走(確定有沒有完成使命也很簡單,直接叫主人來認)。你雖然完成了使命,但是仍然身手敏捷躲過一劫,藏進了幸存區(qū);這是妖精修煉的地方,你來的時候看到還有其他的妖精已經(jīng)在里面了。

新創(chuàng)建的對象優(yōu)先存放在年輕代的伊甸園區(qū),存放滿了就會觸發(fā)Minor GC清理,清理年輕代中不再被引用到的對象(可達性分析:從GC Roots開始向下收索,搜索走過的路徑成為引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,那么虛擬機就判斷是可回收對象。當然也有其他的算法);有一些清理不掉的就移入幸存區(qū)。

幸存區(qū)Survivor

幸存區(qū)是個神奇的地方,自由伊甸園的1/8(默認),傳聞幸存區(qū)有兩個,它們一模一樣,一個出現(xiàn)的時候另一個隱藏;每當洪水來臨它們就會互相交換位置,而在這里修煉的妖精如果沒有及時找到另一個幸存區(qū),也會被大洪水沖走。

這一天大洪水又來了,大家都在著急尋找另一個幸存區(qū)的時候,你發(fā)現(xiàn)老虎精一點也不慌張,原來他已經(jīng)渡劫了15次,可以進入魔界了;這時大洪水把那些還沒找到幸存區(qū)的妖精全部沖走了,再另一個幸存區(qū)你看到有剛和你一起逃過來的妖精和一些新面孔,這些新面孔是從伊甸園逃過來的動物。

幸存區(qū)有兩個,但只用一個,另一個要保持空著,每次清理的時候切換幸存區(qū);空著是為了裝下次清理不掉的垃圾,還有給老垃圾計數(shù),默認進入老年代的閾值是15次;每個幸存區(qū)的默認大小是伊甸園的1/8。

老年代 Old Gen

終于你也歷經(jīng)了15次劫難,進入了魔界;這個地方時間過得非常慢;外界輪回了幾百次,這里還是老樣子,偶爾有一些入魔的妖精進來。

不知過去了多久,這一天魔界有了大動靜,上帝造了一只鯤,地界用洪水沖了一遍還是放不下;直接放進了魔界,雖然魔界是地界定兩倍(默認),但是這個鯤還是太大了;魔界很快就將會被裝滿了。

果不其然。上帝在用大洪水清理完地界后,有一些妖精進入魔界時發(fā)生了點意外,原來魔界終于滿了;由于魔物對洪水已經(jīng)免疫力,所以上帝使用了圣光裁決對魔界進行清洗;花費了漫長的時間,是洪水的10倍;圣光裁決之前必然會發(fā)生大洪水。

不知道什么原因,你又僥幸的活了下來;但是大部分魔物都被清理掉了,看著空蕩蕩的魔界,又可以過很長一段時間的悠閑日子了。

老年代的操作沒那么頻繁,默認大小是年輕代的兩倍;年輕代裝不下的對象也會直接存放在老年代;老年代內(nèi)存滿了會調(diào)用Major GC清理老年代,但是時間比清理年輕代要長10倍;如果老年代也裝不下就會報異常,內(nèi)存溢出OOM。

整體收集 Full GC

就這樣過了很久很久,魔界又快滿了;地界有一只大象修煉成魔了,但是魔界又容不下他,下不去又上不來;驚動了上帝,出現(xiàn)了這種不合規(guī)定存在,只能啟動審判日,清理整個世界!審判日持續(xù)時間會很長很長,大多數(shù)情況都要避免使用審判日。

關(guān)于整堆收集:每次開始老年代收集之前必定會先觸發(fā)年輕代收集,所以清理老年代的時候基本等于整堆收集了,所以Full GC=Young GC+Old GC?

Full GC還是和Young GC+Old GC有點區(qū)別,如果執(zhí)行Young GC時需要移動到老年代的對象大于老年代剩余空間,這種情況就只有Full GC可以解決。

調(diào)用System.gc()也會執(zhí)行Full GC。

內(nèi)存溢出(out of memory)OOM

本來以為世界清靜了,可惜世事難料,上帝一次性造了3只鯤,地界和魔界都裝不下了,世界毀滅了,OOM。


GC算法

沒用在java中的引用計數(shù)法

對象被引用+1,解除引用-1;碰上互相引用就沒法處理了。

可達性分析算法,大佬認領(lǐng)法

使用GCRoots(下面會有介紹),GC根集合,從根出發(fā),尋找被引用的對象;剩下的沒被引用的就是垃圾。


標記法, 有了GCRoots怎么標記對象?

從根開始尋找引用的對象,還有引用對象引用的對象。

多標/漏標:由于大部分GC都是并行發(fā)生,所以有些對象正在被使用,標記的時候被引用,標記結(jié)束后引用已經(jīng)解除了,這就是多標,反過來叫漏標;多標下次GC再清理就好,而漏標的危害更大,被引用的對象當作垃圾清理,會出現(xiàn)嚴重問題。

重標:就是在完成一次標記后,STW(全部暫停),再檢查一邊;由于之前已經(jīng)并行完成了大多數(shù)工作,所以暫停時間不會很長。

不管是哪種收集器都會這樣。

標記之后就該清理了

清除算法

最快,最不負責任的算法;簡單的把垃圾刪了就行。缺點:導(dǎo)致有很多碎片。


復(fù)制算法

復(fù)制算法需要有一塊空內(nèi)存,就像幸存1區(qū)和幸存2區(qū)一樣;刪除垃圾后,把存活對象移到新空間。缺點:占兩倍空間。


整理算法

刪除之后再整理。缺點:費時間,復(fù)雜度高。



G1收集器,垃圾回收的平行世界

把內(nèi)存分成很多區(qū)塊,每塊都有各自的年輕帶/老年帶,充分利用了多核CPU,硬件進步帶來的好處;

整體使用整理法,局部使用復(fù)制法(白色空內(nèi)存就可以使用復(fù)制法),沒有碎片。

計算分區(qū)垃圾量,回收多的。


Humongous是專門存大件的。

被淘汰的CMS收集器

使用標記清除法,導(dǎo)致有很多碎片,最后不得不使用Full GC來處理,漫長的停頓。對CPU敏感。

其他

GC垃圾回收在JAVA8以后只有青年代和老年代,之前還有永久代。

收集器:

年輕代收集(Minor GC / Young GC): 只是年輕代的垃圾收集

老年代收集 (Major GC / Old GC): 只是老年代的垃圾收集 (CMS GC 單獨回收老年代)

混合收集(Mixed GC):收集整個新生代及老年代的垃圾收集 (G1 GC會混合回收, region區(qū)域回收)

整堆收集(Full GC):收集整個java堆和方法區(qū)的垃圾收集器

GC Roots的對象有:

虛擬機棧(棧幀中的本地變量表)中引用的對象

方法區(qū)中類靜態(tài)屬性引用的對象。

方法區(qū)中常量引用的對象。譬如字符串常量池里的引用

本地方法棧中引用的對象

所有被同步鎖持有的對象

分代收集時,將其他代的引用對象臨時性加入GC Roots


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容