1. 程式人生 > >JVM--13 【垃圾回收機制】 如何判斷物件是垃圾

JVM--13 【垃圾回收機制】 如何判斷物件是垃圾

一、物件已經死了嗎?

          在堆裡面存放著Java世界中幾乎所有的物件例項,垃圾收集器在對堆進行回收前,第一件事情就是確定這些物件之中哪些是“存活”著,哪些已經“死去”(即不可能再被任何途徑使用的物件)

判斷物件是否存活的演算法:引用計數法、可達性分析演算法。這裡分兩篇博文介紹一下這兩種演算法,本篇先介紹一下:引用計數演算法

二、引用計數演算法    

  引用計數演算法:給物件中新增一個引用計數器,每當有一個地方引用它時,計數器值就+1;當引用失效時,計數器值就-1;任何時刻計數器為0的物件就是不能再被使用的垃圾物件。

            引用計數演算法的實現簡單,判定效率高。在大部分情況下它都是一個不錯的演算法。但是,至少主流的Java虛擬機器裡面沒有選用引用計數演算法來管理記憶體,主要原因是它很難解決物件之間的相互迴圈引用的問題,這樣就會造成記憶體洩漏。

下面舉例說明:

            下面程式碼中物件g1和g2都有欄位instance,賦值使g1.instance = g2  g2.instance = g1 除此之外,這兩個物件再無任何引用,實際上這兩個物件已經不可能再被訪問,但是它們之間相互引用著對方,導致它們的引用計數都不為0,於是引用計數演算法無法通知GC收集器回收它們。

package cc.mynatapp.detroitdentist.gc;

/**
 * @author Xu hao
 * @Description 判斷物件是否存活演算法:引用計數
 * 需要設定JVM引數:
 *      -verbose:gc    列印垃圾回收資訊
 *      -xx:+PrintGCDetails 列印垃圾回收的詳細資訊
 * @Version 1.0
 * Email 
[email protected]
* create on 2018/9/22 */ public class GCStrategyRefCount { private Object instance; /** * 這裡構造器的唯一意義就是佔用點記憶體,以便能在GC日誌中看清楚是否被回收過。 **/ public GCStrategyRefCount() { byte [] g = new byte[20 * 1024 * 1024]; } public static void main(String[] args){ GCStrategyRefCount g1 = new GCStrategyRefCount(); GCStrategyRefCount g2 = new GCStrategyRefCount(); // g1 g2 迴圈引用 g1.instance = g2; g2.instance = g1; // 斷掉Java棧中g1 g2的reference g1 = null; g2 = null; System.gc(); } }

               執行結果如下:

               從上面的結果中可以清楚看到,GC日誌中包含“21054K->574K(31428K)”,這意味著虛擬機器並沒有因為這兩個物件相互引用就不回收它們,這也從側面說明HotSpot並沒有通過引用計數演算法來判斷物件是否為垃圾物件(物件是否存活)。