以前看過了垃圾回收算法的新生代GC,也是使用的一種比較浪費內存的複製算法,晚上看書又接着往下看了一點,面試
堆 = 新生代+老年代,可是要注意一點老年代不包括永久代(方法區),也就是說堆內存中只有新生代和老年代,而永久代是指的方法區。算法
以前介紹過新生代中的垃圾回收機制了,再來介紹一下老年代的垃圾回收機制裏面使用到的算法。數組
新生代GC:MinorGC 以前介紹過了不說了,複製算法圖解也比較清晰線程
老年代GC:FullGC 咱們先說FullGC出現的緣由吧,FullGC是老年代的GC,在新生代若是說存在的對象或者說新建立 出來的對象因爲某些緣由須要移動到老年代中,可是老年代中壓根就沒有這麼大的內存空間去容納這個對象, 那麼就會引起一次FullGC,若是在執行完FullGC以後,仍是沒有辦法給這些對象分配內存,那麼涼了,該拋出異常了,異常類型就是OutOfMemoryError。3d
而FullGC使用的是和MinorGC不同的算法,它使用的是標記清除算法,聽名字,挺好理解的,來波圖示解析一波。 深刻了解JVM一書中的圖示是這個樣子的,xml
看名字的話是先標記,而後在刪除。這也是也給最最基本的算法。 這個算法就是分兩個步驟對象
•標記(Mark)過程:找到全部的能夠訪問的對象,作個指定的標記。blog
•清除(Swep)過程:遍歷堆內存,把未標記的對象進行一個回收。遞歸
以前看一些文檔上說,先標記,而後把沒有標記的對象給回收掉,其實意思都差很少,可是在深刻理解JVM一書中說到,首先標記出全部的須要回收的對象,在標記完成以後統一回收全部的被標記的對象, 其實個人理解和書中感受有點不太同樣,不過區別也不大。我說說個人理解吧。內存
在瞭解了這個以後,咱們還得說一個概念,那就是GC Root,Root咱們能夠理解成一個根節點就像這個樣子
上圖中的a,b,c,d,就是活着的對象,若是說存在這引用,好比說b引用的a,那麼a他就是屬於活着的對象。 當咱們老年代內存區中的有效的內存空間不夠的時候,那麼這時候整個世界都要安靜下來了(stop the world),這時候就要開始準備進行垃圾回收了。
•標記:遍歷全部的GC Roots,而後將全部GC Roots可達的對象標記爲存活的對象。就是咱們圖中所標記的a,b,c,d.•清除:清除的過程將遍歷堆中全部的對象,將沒有標記的對象所有清除掉。 也就是說,若是內存不夠,GC線程就會被觸發而後將程序暫停,隨後將依舊存活的對象標記一遍,最後再將堆中全部沒被標記的對象所有清除掉,接下來便讓程序繼續恢復運行。
流程圖就想這個樣子的 初始下的老年代中的對象狀態
這時候都是沒有被標記的狀態,接下來內存不夠,GC線程中止,開始進行標記了
按照根節點開始遍歷 標記的abcdeh都是存活的對象,接下來開始標記。
接下來就是清除數據了,這個就更加的簡單了
清楚完成以後還有就是把標記去除掉,能夠下次進行標記清除的時候繼續清除
這樣標記清除就執行完畢了,剩下還有兩個要說的地方,
一是在進行標記清楚算法的時候爲何要讓程序中止,(stop the world)。
二是標記清除算法的優勢和缺點又是什麼?(Stop the World)
程序中止其實能夠理解,由於若是說不中止程序的話,咱們在標記完成這個b對象以後,咱們new出一個新的對象J,能夠從B指向J,也就是說,這時候J應該是被標記的狀態,可是實際狀況確定不是,這個對象在B標記完以後,立刻都要結束了,咱們又new出來一個對象,可想而知,他確定是沒有被標記的,因此在第二階段進行清除的時候,這個苦命的J將會被清除掉, 那這樣確定是不符合咱們的實際狀況的。
你想呀這剛剛new出來的對象結果被清除了,突然變成了空值,那就不符合咱們的要求了。因此他會讓程序先中止,而後不會再出現這種狀況,而後進行開始標記階段。
首先咱們能夠先看缺點,他的缺點很是明顯,
•由於他會遞歸遍歷Root,這樣的話 Stop the World的時間就比較長了,這樣一直讓人等待的滋味可不是那麼好受。•第二個就是這種清除方式清除出來的內存空間是不連續的,你看這個圖
死亡的上下分紅了2部分,是不連續的,這樣給JVM又形成了一種額外的負擔,他須要去維持一個內存的空閒列表,若是說咱們在這時候去new一個數組,你想一想他去找這個連續的內存空間的話,是否是就要困難不少呢?
他的優勢也有,
•好比說不會出現循環引用, 咱們能夠想一想 兩個類 互相引用,A中newB,B中newA,那這樣豈不是a.b=b ,b.a = a ,是吧 ,而標記清除算法在走完了以後,是能夠回收a,和b的,由於他是從根元素開始遍歷標記,也就是從ab開始,那麼單一的a和單一的b就是沒有被標記的,因此,這樣就避免了循環引用的問題•還有一點感受沒啥區別,都是內存不夠的時候才進行的引用。這沒啥說的。
而由於標記--清除算法會致使內存分配都出現了各類不均勻的空間,這時候就有了另外的一種算法,直接把那些存活的對象標記出來,而後給他懟到內存空間邊界,而後剩下的直接全給他清除了。這方法圖解看的一清二楚,剩下的都是和標記清除算法同樣的,好像沒啥解釋的,直接上圖
書中你看就是把存活的都給懟到內存空間的上邊,你也能夠隨便的理解成上下左右都ok。
以上就是堆內存中的老年代的兩種垃圾回收算法了,若是有不合適的,但願大佬能夠指正,一塊兒討論一下。
Java 極客技術公衆號,是由一羣熱愛 Java 開發的技術人組建成立,專一分享原創、高質量的 Java 文章。若是您以爲咱們的文章還不錯,請幫忙讚揚、在看、轉發支持,鼓勵咱們分享出更好的文章。
關注公衆號,你們能夠在公衆號後臺回覆「博客園」,免費得到做者 Java 知識體系/面試必看資料。