1. 程式人生 > 實用技巧 >JVM垃圾回收相關演算法

JVM垃圾回收相關演算法

垃圾標記階段

  • 物件存活判斷:在堆裡存放著幾乎所有的Java物件例項,在GC執行垃圾回收之前,首先需要區分出記憶體中哪些是存活物件,哪些是已經死亡的物件。只有被標記為己經死亡的物件,GC才會在執行垃圾回收時,釋放掉其所佔用的記憶體空間,因此這個過程我們可以稱為垃圾標記階段。
  • 那麼在JVM中究竟是如何標記一個死亡物件呢?簡單來說,當一個物件已經不再被任何的存活物件繼續引用時,就可以宣判為已經死亡
  • 判斷物件存活一般有兩種方式:引用計數演算法和可達性分析演算法。

引用計數法 (java沒有采用)

  • 引用計數演算法(Reference Counting)比較簡單,對每個物件儲存一個整型 的引用計數器屬性。用於記錄物件被引用
    的情況。
  • 對於一個物件A,只要有任何一個物件引用了A,則A的引用計數器就加1;當引用失效時,引用計數器就減1。只要物件A的引用計數器的值為0,即表示物件A不可能再被使用,可進行回收。
  • 優點:實現簡單,垃圾物件便於辨識;判定效率高,回收沒有延遲性。
  • 缺點:
    • ➢它需要單獨的欄位儲存計數器,這樣的做法增加了儲存空間的開銷。
    • ➢每次賦值都需要更新計數器,伴隨著加法和減法操作,這增加了時間開銷。
    • ➢引用計數器有一個嚴重的問題,即無法處理迴圈引用的情況。這是一 條致命缺陷,導致在Java的垃圾回收器中沒有使用這類演算法。

迴圈引用導致的記憶體洩漏情況:

證明:java使用的不是引用計數演算法 示例程式碼:
/**
 * -XX:+PrintGCDetails
 * 證明:java使用的不是引用計數演算法
 */
public class RefCountGC {
    //這個成員屬性唯一的作用就是佔用一點記憶體
    private byte[] bigSize = new byte[5 * 1024 * 1024];//5MB,僅僅意味著只要建立我這個RefCountGC物件例項,就會佔用5M的堆空間

    Object reference = null;

    public static void main(String[] args) {
        RefCountGC obj1 
= new RefCountGC(); RefCountGC obj2 = new RefCountGC(); obj1.reference = obj2; obj2.reference = obj1; obj1 = null; obj2 = null; //顯式的執行垃圾回收行為 //這裡發生GC,obj1和obj2能否被回收?能,證明Java沒有采用引用計數演算法 System.gc(); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } } }

引用計數法的弊端:即使把obj1 一reference和obj2 一reference(棧引用)置null。 則在Java堆當中的兩塊記憶體依然保持著互相引用,無法回收。

但是把obj1 一reference和obj2 一reference置為null之後,如果顯示的去呼叫System.gc();這裡將會發生GC,回收堆中bj1和obj2實體。從而證明Jlava沒有采用引用計數演算法 .