JVM--13 【垃圾回收機制】 如何判斷物件是垃圾
阿新 • • 發佈:2018-12-11
一、物件已經死了嗎?
在堆裡面存放著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並沒有通過引用計數演算法來判斷物件是否為垃圾物件(物件是否存活)。