淺談java GC的幾種算法

最近在看<<深入理解java虛擬機>>這本書,對垃圾收集算法這部分內容做個筆記整理下。
java的運行時數據區如下,其實就是堆,棧,方法區,其中棧又分爲虛擬機棧,本地方法棧,程序計數器,並且棧是屬於線程私有,堆跟方法區是線程共享的。
幾乎所有的對象都是存放在堆中的,所以java堆是垃圾收集器管理的主要區域。
在這裏插入圖片描述

可達性分析算法
通過一些GC Roots作爲起點,當一些對象沒有任何引用鏈能夠到達時,則證明該節點是不可用,是需要回收的,也就是通過GC Roots無法到達的對象就是可回收對象。
在這裏插入圖片描述

如何進行垃圾回收
找到了需要回收的對象之後,接下來就是要進行垃圾回收,那麼垃圾收集有哪些算法呢

1.標記-清除算法
分爲兩步走,首先把內存中的對象進行標記,然後把可回收的單獨拿走,這樣的缺點是會產生大量的內存碎片,下次如果有有大對象創建的時候就會發現內存不夠,又觸發一次垃圾回收,但是實際上內存是夠的,只是不能連續使用,因爲我們申請內存空間的時候是需要連續的一片。
在這裏插入圖片描述
2.複製算法
將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後清除掉使用過的這一半。但是代價就是內存利用率太低。

在這裏插入圖片描述
3.標記-整理算法
標記過程仍然與標記 — 清除算法一樣,然後讓所有存活的對象都向一端移動,再清理掉端邊界以外的內存區域。

標記整理算法在標記-清除算法上做了升級,解決了內存碎片的問題,也規避了複製算法只能利用一半內存區域的弊端,但它對內存變動更頻繁,需要整理所有存活對象的引用地址,在效率上比複製算法要差很多。

在這裏插入圖片描述
4.分代收集算法
分代收集算法,是融合上述3種基礎的算法思想,而產生的針對不同情況所採用不同算法的一套組合。根據對象存活週期的不同將內存劃分爲幾塊。一般是把 Java 堆分爲新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象是待回收的,只有少量存活,那就選用複製算法,只需要付出少量存活對象的複製成本就可以完成收集。而老年代中因爲對象存活率高,就使用標記-清除或標記-整理算法來進行回收。

java堆的劃分
java堆分爲2個區域,新生代和老年代,比例爲1:2,而新生代又分爲Eden區和Survivor區,Survivor 區又分爲from區和to區,有些也稱爲s0,s1區,新生代的各區比例如下:8:1:1
在這裏插入圖片描述
對象在Eden區進行創建,當 Eden 區沒有足夠空間進行分配時,虛擬機會發起一次 Minor GC。通過 Minor GC 之後,Eden 會被清空,Eden 區中絕大部分對象會被回收,而那些無需回收的存活對象,將會進到 Survivor 的 From 區(若 From 區不夠,則直接進入 Old 區)。然後到下一次 Minor GC的時候,又會把Eden區存活的對象和from區存活的對象複製到to區,然後清理掉Eden區跟from區,這樣不斷來回複製,經歷15次 Minor GC 還能在新生代中存活的對象,纔會被送到老年代。爲什麼是15次呢,因爲對象頭有一個地方是標記對象的年齡的,用2進制來表示,初始化是0000,一次之後就是0001,最後一次是1111,恰好可以標記15次。

老年代佔據着2/3的堆內存空間,只有在 Major GC 的時候纔會進行清理,每次 GC 都會觸發“Stop-The-World”。內存越大,STW 的時間也越長,所以內存也不僅僅是越大就越好。由於老年代的對象存活較高,所以老年代使用標記 - 整理算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章