GC 主要的四種算法

程序在運行過程中,會產生大量的內存垃圾(一些沒有引用指向的內存對象都屬於內存垃圾,因爲這些對象已經無法訪問,程序用不了它們了,對程序而言它們已經死亡),爲了確保程序運行時的性能,java虛擬機在程序運行的過程中不斷地進行自動的垃圾回收(GC)。關於 JVM 的 GC 算法主要有下面四種:

引用計數算法(Reference counting)

算法思想:

每個對象在創建的時候,就給這個對象綁定一個計數器。每當有一個引用指向該對象時,計數器加一;每當有一個指向它的引用被刪除時,計數器減一。這樣,當沒有引用指向該對象時,該對象死亡,計數器爲0,這時就應該對這個對象進行垃圾回收操作。

核心思想:

爲每個對象額外存儲一個計數器 RC ,根據 RC 的值來判斷對象是否死亡,從而判斷是否執行 GC 操作。

優點:

簡單
計算代價分散
“幽靈時間”短(幽靈時間指對象死亡到回收的這段時間,處於幽靈狀態)
缺點:

不全面(容易漏掉循環引用的對象)
併發支持較弱
佔用額外內存空間
例子如圖:

初始狀態: 
 
改變引用後: 


標記–清除算法(Mark-Sweep)

算法思想:

爲每個對象存儲一個標記位,記錄對象的狀態(活着或是死亡)。分爲兩個階段,一個是標記階段,這個階段內,爲每個對象更新標記位,檢查對象是否死亡;第二個階段是清除階段,該階段對死亡的對象進行清除,執行 GC 操作。

優點

最大的優點是,相比於引用計數法,標記—清除算法中每個活着的對象的引用只需要找到一個即可,找到一個就可以判斷它爲活的。

此外,這個算法相比於引用計數法更全面,在指針操作上也沒有太多的花銷。更重要的是,這個算法並不移動對象的位置(後面倆算法涉及到移動位置的問題)。

缺點

很長的幽靈時間,判斷對象已經死亡,消耗了很多時間,這樣從對象死亡到對象被回收之間的時間過長。

每個活着的對象都要在標記階段遍歷一遍;所有對象都要在清除階段掃描一遍,因此算法複雜度較高。

沒有移動對象,導致可能出現很多碎片空間無法利用的情況。

例子如圖

這個圖中,圓圈內灰色的對象就是已經死亡的對象,被標記爲死亡,等待清除。 


標記–整理算法

算法思想

標記-整理法是標記-清除法的一個改進版。同樣,在標記階段,該算法也將所有對象標記爲存活和死亡兩種狀態;不同的是,在第二個階段,該算法並沒有直接對死亡的對象進行清理,而是將所有存活的對象整理一下,放到另一處空間,然後把剩下的所有對象全部清除。這樣就達到了標記-整理的目的。

優點

該算法不會像標記-清除算法那樣產生大量的碎片空間。
缺點

如果存活的對象過多,整理階段將會執行較多複製操作,導致算法效率降低。
例子

如圖: 


上面是標記階段,下面是整理之後的狀態。可以看到,該算法不會產生大量碎片內存空間。

複製算法

算法思想

該算法將內存平均分成兩部分,然後每次只使用其中的一部分,當這部分內存滿的時候,將內存中所有存活的對象複製到另一個內存中,然後將之前的內存清空,只使用這部分內存,循環下去。

注意: 
這個算法與標記-整理算法的區別在於,該算法不是在同一個區域複製,而是將所有存活的對象複製到另一個區域內。

優點

實現簡單
不產生內存碎片
缺點

每次運行,總有一半內存是空的,導致可使用的內存空間只有原來的一半。
總結

不同算法有不同的優點和缺點,除了引用計數法不常用外,其他三種算法在現在的java虛擬機上也是很常見的,間接說明了這幾個經典算法還是有其適用性的。

理解 JVM 的 GC 算法能夠幫助我們更好地理解java的垃圾回收機制,例如,在 JVM 的年輕代使用的是複製算法來進行垃圾回收(由於其中的存活對象比例較小);而在老年代,使用的卻是標記-清除法或標記-整理法(由於每次回收都只回收少量對象)
 

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