關(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