JVM垃圾回收及其算法

什麼是垃圾回收?

垃圾回收(Garbage Collection,GC),顧名思義就是釋放垃圾佔用的空間,防止內存泄露。有效的使用可以使用的內存,對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收。

垃圾回收的常見算法:

引用計數法、標記清除法、標記壓縮法、複製算法、分代算法

引用計數法:

給每個對象設置一個計數器,當有地方引用該對象時計數器+1,引用失效時計數器-1。用計數器是否爲0來判斷該對象是否可被回收。

優點:

  • 實時性較高,不用等到內存不夠時纔開始進行垃圾回收,運行時可以根據計數器是否爲0來回收。

  • 在垃圾回收的過程中,應用無需掛起,如果申請內存時內存不足直接報outofmember錯誤。

  • 區域性,更新對象的計數器時,只是影響到該對象,不會掃描全部對象。

缺點:

  • 每次對象被引用時,都需要更新計數器有時間開銷

  • 浪費CPU資源,即使內存夠用,仍然在運行時進行計數器的統計

  • 無法解決循環引用的問題(最大的缺點)

我們來看下面這個例子:

public class ReferenceCountingGC {

  public Object instance;

  public ReferenceCountingGC(String name) {
  }

  public static void testGC(){

    ReferenceCountingGC a = new ReferenceCountingGC("objA");
    ReferenceCountingGC b = new ReferenceCountingGC("objB");

    a.instance = b;
    b.instance = a;

    a = null;
    b = null;
  }
}

最後這兩個對象都不能被訪問了,但是由於他們相互引用導致計數器不爲0,通過引用計數法永遠無法通知GC收集器回收。

標記清除法

兩個階段:標記、清除

標記清除算法(Mark-Sweep)是最基礎的一種垃圾回收算法,它分爲兩個階段。先把內存中的對象進行標記,哪些屬於可回收的標記出來,然後把這些垃圾清理掉,如上圖所示。清理掉的內存空間就會變爲未使用的內存區域。但是問題也很顯而易見,存在內存碎片。

優點:

  • 解決了引用計算算法中的循環引用的問題,沒有從root節點引用的對象都會被回收

缺點:

  • 效率較低,標記和清除兩個動作都要遍歷所有的對象,並且在GC是,需要停止應用程序,對與交互性要求比較高的應用而言這個體驗是非常差的。

  • 碎片化嚴重

標記壓縮算法

標記壓縮算法是在標記清除算法的基礎之上做了優化的算法。和標記清除算法一樣,從根節點開始對對象的引用進行標記,然後將存活的對象壓縮到內存的一端,然後清理邊界以外的垃圾,從而解決了碎片化的問題。但是標記整理算法對內存變動更頻繁,需要整理所有存活對象的引用地址,在效率上比複製算法要差很多。

複製算法

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

這樣使得每次都是對其中的一塊進行內存回收,內存分配時也就不用考慮內存碎片等複雜情況,只要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。

缺點:

  • 效率問題: 在對象存活率較高時,複製操作次數多,效率降低

  • 空間問題:內存縮小了一半;需要額外空間做分配擔保(老年代)

jvm中年輕代的內存空間使用的就是複製算法

分代算法

前面介紹的每一種算法都有自己的優缺點,誰都不能替代誰,所以根據垃圾回收對象的特點進行選擇纔是明智的選擇。

分代算法中,把Java堆分爲新生代和老年代,年輕代使用複製算法,老年代適合使用標記清除算法或者標記壓縮算法。

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