JVM垃圾回收算法

什麼是垃圾回收

程序的運行必然須要申請內存資源,無效的對象資源若是不及時處理就會一直佔有內存資源,最終將致使內存溢出,因此對內存資源的管理是很是重要了。
爲了讓程序員更專一於代碼的實現,而不用過多的考慮內存釋放的問題,因此,在Java語言中,有了自動的垃圾回收機制,也就是咱們熟悉的GC。程序員

有了垃圾回收機制後,程序員只須要關心內存的申請便可,內存的釋放由系統自動識別完成。換句話說,自動的垃圾回收的算法就會變得很是重要了,若是由於算法的不合理,致使內存資源一直沒有釋放,一樣也可能會致使內存溢出的。算法

垃圾回收的常見算法

自動化的管理內存資源,垃圾回收機制必需要有一套算法來進行計算,哪些是有效的對象,哪些是無效的對象,對於無效的對象就要進行回收處理。
常見的垃圾回收算法有:引用計數法、標記清除法、標記壓縮法、複製算法、分代算法等。編程

引用計數法

引用計數是歷史最悠久的一種算法,最先George E. Collins在1960的時候首次提出,50年後的今天,該算法依然被不少編程語言使用jvm

1.原理
假設有一個對象A,任何一個對象對A的引用,那麼對象A的引用計數器+1,當引用失敗時,對象A的引用計數器就-1,若是對象A的計數器的值爲0,就說明對象A沒有引用了,能夠被回收。
1.png編程語言

2.優缺點優化

優勢:spa

  • 實時性較高,無需等到內存不夠的時候,纔開始回收,運行時根據對象的計數器是否爲0,就能夠直接回收
  • 在垃圾回收過程當中,應用無需掛起。若是申請內存時,內存不足,則馬上報outofmember 錯誤
  • 區域性,更新對象的計數器時,只是影響到該對象,不會掃描所有對象

缺點:線程

  • 每次對象被引用時,都須要去更新計數器,有一點時間開銷
  • 浪費CPU資源,即便內存夠用,仍然在運行時進行計數器的統計
  • 沒法解決循環引用問題(最大的缺點)

什麼是循環引用?code

class TestA{
    public TestB b;
}
class TestB{ 
    public TestA a;
}
public class Test {
    public static void main(String[] args) {
        TestA a=new TestA();
        TestB b=new TestB();
        a.b=b;
        b.a=a;
        a=null;
        b=null;
    }
}

雖然a和b都爲null,可是因爲a和b存在循環引用,這樣a和b永遠都不會被回收。
2.png對象

標記清除法

標記清除算法,是將垃圾回收分爲2個階段,分別是標記和清除。

  • 標記:從根節點開始標記引用的對象
  • 清除:未被標記引用的對象就是垃圾對象,能夠被清理

1.原理
1.png
這張圖表明的是程序運行期間全部對象的狀態,它們的標誌位所有是0(也就是未標記,如下默認0就是未標記,1爲已標記),假設這會兒有效內存空間耗盡了,JVM將會中止應用程序的運行並開啓GC線程,而後開始進行標記工做,按照根搜索算法,標記完之後,對象的狀態以下圖。

2.png
能夠看到,按照根搜索算法,全部從root對象可達的對象就被標記爲了存活的對象,此時已經完成了第一階段標記。接下來,就要執行第二階段清除了,那麼清除完之後,剩下的對象以及對象的狀態以下圖所示。

3.png
能夠看到,沒有被標記的對象將會回收清除掉,而被標記的對象將會留下,而且會將標記位從新歸0。接下來就不用說了,喚醒中止的程序線程,讓程序繼續運行便可。

2.優缺點
能夠看到,標記清除算法解決了引用計數算法中的循環引用的問題,沒有從root節點引用的對象都會被回收。一樣,標記清除算法也是有缺點的:

  • 效率較低,標記和清除兩個動做都須要遍歷全部的對象,而且在GC時,須要中止應用程序,對於交互性要求比較高的應用而言這個體驗是很是差的。
  • 經過標記清除算法清理出來的內存,碎片化較爲嚴重,由於被回收的對象可能存在於內存的各個角落,因此清理出來的內存是不連貫的。

1.png
2.png

標記壓縮算法

標記壓縮算法是在標記清除算法的基礎之上,作了優化改進的算法。和標記清除算法同樣,也是從根節點開始,對對象的引用進行標記,在清理階段,並非簡單的清理未標記的對象,而是將存活的對象壓縮到內存的一端,而後清理邊界之外的垃圾,從而解決了碎片化的問題。

1.原理
1.png

2.優缺點
優缺點同標記清除算法,解決了標記清除算法的碎片化的問題,同時,標記壓縮算法多了一步,對象移動內存位置的步驟,其效率也有有必定的影響。

複製算法

複製算法的核心就是,將原有的內存空間一分爲二,每次只用其中的一塊,在垃圾回收時,將正在使用的對象複製到另外一個內存空間中,而後將該內存空間清空,交換兩個內存的角色,完成垃圾的回收。

若是內存中的垃圾對象較多,須要複製的對象就較少,這種狀況下適合使用該方式而且效率比較高,反之,則不適合。
1.png

1.JVM中年輕代內存空間
4.png

  • 在GC開始的時候,對象只會存在於Eden區和名爲「From」的Survivor區,Survivor區「To」是空的。
  • 緊接着進行GC,Eden區中全部存活的對象都會被複制到「To」,而在「From」區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到必定值(年齡閾值,能夠經過-XX:MaxTenuringThreshold來設置)的對象會被移動到年老代中,沒有達到閾值的對象會被複制到「To」區域。
  • 通過此次GC後,Eden區和From區已經被清空。這個時候,「From」和「To」會交換他們的角色,也就是新的「To」就是上次GC前的「From」,新的「From」就是上次GC前的「To」。無論怎樣,都會保證名爲To的Survivor區域是空的。
  • GC會一直重複這樣的過程,直到「To」區被填滿,「To」區被填滿以後,會將全部對象移動到年老代中。

2.優缺點
優勢:

  • 在垃圾對象多的狀況下,效率較高
  • 清理後,內存無碎片

缺點:

  • 在垃圾對象少的狀況下,不適用,如:老年代內存
  • 分配的2塊內存空間,在同一個時刻,只能使用一半,內存使用率較低

分代算法

前面介紹了多種回收算法,每一種算法都有本身的優勢也有缺點,誰都不能替代誰,因此根據垃圾回收對象的特色進行選擇,纔是明智的選擇。

分代算法其實就是這樣的,根據回收對象的特色進行選擇,在jvm中,年輕代適合使用複製算法,老年代適合使用標記清除或標記壓縮算法。

相關文章
相關標籤/搜索