JVM 系列文章之 物件存活分析 - 引用計數 and 可達性分析

amadan發表於2021-09-09

前言

在垃圾回收器回收物件時,我們如何判斷哪些物件是"活"的,哪些是"死"的,關於判斷物件存活的方法主要分為兩類: 引用計數法 和 可達性分析演算法

引用計數法

引用計數器就是: 給物件中新增一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減一;任何時刻計數器為 0 的物件就是不可能再被使用的,可以此時進行回收。

但是引用計數法有一個很大的缺陷,就是它很難解決物件之間相互迴圈引用的問題。

可達性分析演算法

在主流的商用程式語言(Java,C#等)的主流實現中,都是稱透過可達性分析來判斷物件是否存活的

R大在知乎上有關可達性分析演算法的回答是:

Tracing GC 的根本思路就是: 給定一個集合的引用作為根出發,透過引用關係遍歷物件圖,能被遍歷到的(可達到的)物件就被判定為存活,其餘物件 (也就是沒有被比遍歷到的)就自然被判定為死亡,注意的是: tracing GC的本質是透過找出所有活物件來把其餘空間認定為"無用",而不是找出所有死掉的物件並回收它們佔用的空間

這裡的集合的引用就是 "GC Roots",所謂 "GC Roots",或者說tracing GC的"根集合",就是一組必須活躍的引用。下圖關於可達性分析演算法的圖示:

圖片描述

gcroots


物件obj4,obj5,obj6雖然相互有關聯,但是它們到 GC Roots是不可達的,所以它們將會判定為可回收的物件


要實現語義正確的tracing GC,就必須要能完整列舉出 所有的GC Roots,否則就可能會漏掃描應該存活的物件,導致GC錯誤回收了這些被漏掃的活物件。

哪些引用是 GC Roots

GC roots對應的引用可能包括:

  • 所有Java執行緒當前活躍的棧幀裡指向 GC堆裡的物件的引用;換句話說,當前所有正在被呼叫的方法的引用型別的引數/區域性變數/臨時值

  • VM的一些靜態資料結構裡指向 GC堆裡的物件的引用,例如說 Hotspot VM裡的Universe裡有很多這樣的引用

  • (看情況)所有當前被載入的Java類

  • (看情況)Java類的引用型別靜態變數

  • (看情況)Java類的執行時常量池裡的引用型別常量(String或Class型別)

  • (看情況)String常量池(StringTable)裡的引用

注意,是一組必須活躍的引用,不是物件

Java可達性分析演算法會不會出現迴圈引用問題?

從前面那張圖中我們就可以看出 GC Roots是單獨出來的,在物件圖之外,以下摘自 R大的回答

GC Root在物件圖之外,是特別定義的"起點",不可能被物件圖內的物件所引用。
一個常見的誤解就是 以為GC Root是一組物件,實際上GC Root通常是一組特別管理的指標,這些指標是 tracing GC 的trace 的起點,它們不是物件圖裡物件,物件也不可能引用到這些"外部"的指標,另外,tracing GC能夠正確處理迴圈引用。保證每個活物件只會被訪問一次就能確定其存活性,物件圖裡是否存在迴圈引用,tracing GC都能判斷物件的存活與否。

這裡談到GC Roots是指標,之前說的是引用,本質它們的意思是一樣的,指標也就是引用。在官方的解釋是:

garbage collection root

A pointer into the Java object heap from outside the heap. These come up, e.g., from static fields of classes, local references in activation frames, etc.

官方的意思是由heap外部指向 heap內的物件的指標,指標另一種說法也是引用。

小結

上面的分析更多的是參考了 R大在知乎上對相應問題的解答,R大是國內JVM巨牛級人物,他的回答都是非常權威的,所以學習 JVM的知識可以多參考 R大的分析。目前本人對 JVM也是一枚渣渣級選手,現在輸出對JVM的一些學習筆記。如有錯誤之處,歡迎指出。



作者:pjmike
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2310/viewspace-2818103/,如需轉載,請註明出處,否則將追究法律責任。

相關文章