本文默認JVM爲HotSpot,在介紹各款垃圾收集器以前先介紹下垃圾收集器的算法,不是講解算法如何實現,而是介紹下算法的處理思想和一些優缺點。算法
垃圾收集器用於清除垃圾的算法有:標記-清除算法、複製算法、標記-整理算法。 用於針對對象不一樣的存活週期而分代的算法有:分代收集算法。這個算法把Java堆分爲新生代和老年代。cdn
標記-清除算法應該是最符合咱們人一開始處理垃圾的思路的算法,例如咱們想清除房間的垃圾,咱們確定是先定位(對應標記)哪些是垃圾,而後把這些垃圾以後扔了(對應清除),簡單粗暴,剩下的不是垃圾的東西我也懶得理,無論了哈哈哈。對象
固然有的人說我打掃房間會先整理不是垃圾的東西而後把垃圾扔了...你走錯片場了請去標記-整理片場(勤勞的孩子)。 blog
這算法有兩個缺點 1.標記和清除的效率不高,按這種思路是一個一個標記過去,而且掃描哪些是標記過得而後才清除了 2.空間碎片問題,看上圖整理後中間空了好多,這樣會使得比較大的對象要申請比較多的連續空間的時候申請不到,明明你空間還很足的。而後致使又一次GC。複製算法等於說根據標記-清除算法的不足之處進行了改進。簡單的來講它把空間切成了兩半,一次我就用一半,一半滿了我就把活着的對象放在另外一半按順序放,而後無腦的把剛纔使用的那一半空間一次清理乾淨,而後保留着存活的那些對象的內存空間換上去使用。這樣就沒了標記-清除算法的空間碎片問題。 內存
虛擬機基本上用這種算法來回收新生代,可是切一半空間利用率過低了,一次就只能用一半。因此在HotSpot中是把這一塊空間分爲3塊,一塊Eden,兩塊Survivor。由於正常狀況下新生代的大部分對象都是短命鬼,因此能活下來的很少,因此默認的空間劃分比例是8:1:1。用法就是每次只使用Eden和一塊Survivor,而後把活下來的對象都扔到另外一塊Survivor。再清理Eden和以前的那塊Survivor。而後再把Eden和存放存活對象的那一塊Survivor用來迎接新的對象。就等於每次回收了以後都會對調一下兩個Survivor。虛擬機
可是事情總有意外,萬一這波對象短命鬼較少,存活下來的不少,那一個Survivor放不下,因此還有個擔保機制,就像咱們現實生活中的擔保人,你還不起了擔保人上!這個擔保人就是老年代,也就是Survivor放不下了就放老年代去。 那爲何虛擬機基本上用這種算法來回收新生代呢?就是由於新生代的對象大部分存活時間不長,因此每次GC的時候複製的比較少,效率高啊,每次就複製一點點對象到Survivor。it
那要是到老年代也就是一些老不死的對象那用複製效率就低了啊,首先8:1:1這種分法就不合適了,由於每次存活下來的對象會不少,1就放不下了,你可能就得"五五開"分了,那"五五開"之分也就算了,由於每次對象基本上都活着,因此每次複製等於複製一半空間的對象。效率低啊。io
還有,新生代有老年代作擔保啊,多了的對象能夠放到老年代,而老年代不行啊,沒有依靠了。因此就又有了下面的算法。class
標記-整理算法的思路也是和標記-清除算法同樣,先標記那些須要清除的對象,可是後續步驟不同,它是整理,對就是像上面說的那些清除房間垃圾每次都會整理的人同樣那麼勤勞。 效率
每次會移動全部存活的對象,且按照內存地址次序依次排列,也就是把活着的對象都像一端移動,而後將末端內存地址之後的內存所有回收。因此用了它也就沒有空間碎片的問題了。這算法就是把Java堆分爲新生代和老年代,這樣好根據每一個代的對象存活時間特色上不一樣的收集算法。 因此通常新老代就是用複製算法。老年代用標記-清除或標記-整理算法。
若有錯誤歡迎指正! 我的公衆號:yes的練級攻略