面試必問:JVM 如何確定死亡對象?

在 JVM 中,有兩個非常重要的知識點,一個是 JVM 的內存佈局(JVM 運行時的數據區域),另一個就是垃圾回收。而垃圾回收中又有兩個重要的知識點,一個是如何確定 JVM 中的垃圾對象,另一個是使用不同的垃圾收集器進行垃圾回收。而本篇要討論的是前者,後面的內容咱們下一篇再聊。

垃圾對象的判定有兩種常用的算法:引用計數器算法和可達性分析算法。

1.引用計數器算法

引用計數算法(Reference Counting) 屬於垃圾收集器的早期實現算法了,它指的是在創建對象時關聯一個與之相對應的計數器,當此對象被使用時加 1,相反銷燬時 -1。當此計數器爲 0 時,則表示此對象未使用,可以被垃圾收集器回收。

引用計數算法的優缺點很明顯,其優點是垃圾回收比較及時,實時性比較高,只要對象計數器爲 0,則可以直接進行回收操作;而缺點是無法解決循環引用的問題,比如以下代碼:

public class RefCounterTest {
    // 對象 A
    static class RefObjectA {
        private RefObjectB refObjectB;

        public void setRefObjectB(RefObjectB refObjectB) {
            this.refObjectB = refObjectB;
        }
    }
    // 對象 B
    static class RefObjectB {
        private RefObjectA refObjectA;

        public void setRefObjectA(RefObjectA refObjectA) {
            this.refObjectA = refObjectA;
        }
    }
    // 測試代碼
    public static void main(String[] args) {
        RefObjectA objectA = new RefObjectA();
        RefObjectB objectB = new RefObjectB();
        objectA.setRefObjectB(objectB);
        objectB.setRefObjectA(objectA);
        objectA = null;
        objectB = null;
    }
}

如以上代碼所示,即使是將 main 方法中的 objectA 和 objectB 都設置爲 null,也就是這兩個對象都徹底不使用了,但是因爲二者存在相互引用的關係,所以它們所對應的對象計數器不爲 0,這樣循環引用導致垃圾數據無法被清除的事件就產生了。

2.可達性分析算法

可達性分析算法(Reachability Analysis) 是目前主流虛擬機中,使用最廣泛的判斷垃圾對象的實現算法,它指的是從對象的起點(GC Roots)開始向下搜索,如果對象到 GC Roots 沒有任何引用鏈相連時,也就是說此對象到 GC Roots 不可達時,則表示此對象可以被垃圾回收器所回收,如下圖所示:
image.png
在 Java 語言中,可作爲根節點(GC Roots)的對象有以下 4 類:

  1. Java 虛擬機棧中的引用對象,也就是 Java 虛擬機棧幀中,本地變量表所存儲的(引用)對象。在 Java 虛擬機棧幀中存儲的對象都是將來執行時,要使用的對象,所以和引用對象相關的對象都不能被回收;
  2. 本地方法棧中的引用對象和 Java 虛擬機棧中的引用對象類似,也不能被回收;
  3. 方法區中類靜態屬性引用的對象也可以作爲 GC Roots;
  4. 方法區中常量引用的對象也可以作爲 GC Roots。因爲常量是保存在常量池中的,屬於全局可使用的對象,所以也能作爲 GC Roots。

3.有關“引用”

不管是引用計數法還是可達性分析算法都與對象的“引用”有關,這說明對象的引用決定了對象的生死,而 Java 中的引用也比較複雜,它從 JDK 1.2 之後,(引用)分成了以下 4 種類型:

  • 強引用:在代碼中普遍存在的,類似 Object obj = new Object() 這類引用,只要強引用還在,垃圾收集器永遠不會回收掉被引用的對象
  • 軟引用:是一種相對強引用弱化一些的引用,可以讓對象豁免一些垃圾收集,只有當 JVM 認爲內存不足時,纔會去試圖回收軟引用指向的對象,JVM 會確保在拋出 OutOfMemoryError 之前,清理軟引用指向的對象
  • 弱引用:非必需對象,但它的強度比軟引用更弱,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前
  • 虛引用:也稱爲幽靈引用或幻影引用,是最弱的一種引用關係,無法通過虛引用來獲取一個對象實例,爲對象設置虛引用的目的只有一個,就是當着個對象被收集器回收時收到一條系統通知

總結

垃圾對象的判定有兩種常用的算法:引用計數器算法和可達性分析算法。其中引用計數器算法實現簡單、運行高效,但是存在循環引用的問題,所以主流的虛擬機使用的都是可達性分析算法,可達性分析算法是從對象的根節點 GC Roots 向下搜索,如果根節點相連就是正常的對象,否則爲垃圾對象可以被垃圾回收器回收。

本文已收錄到 Gitee 開源倉庫《Java 面試指南》,其中包含的內容有:Redis、JVM、併發、併發、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設計模式、消息隊列等模塊。Java 面試有它就夠了:超全 Java 常見面試題,持續更新...

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