您的位置:首頁>正文

Java虛擬機器:如何判定哪些物件可回收?

在堆記憶體中存放著Java程式中幾乎所有的物件實例, 堆記憶體的容量是有限的, Java虛擬機器會對堆記憶體進行管理, 回收已經“死去”的物件(即不可能再被任何途徑使用的物件), 釋放記憶體。 垃圾收集器在對堆記憶體進行回收前, 首先要做的第一件事就是確定這些物件中哪些還存活著, 哪些已經死去。 Java虛擬機器是如何判斷物件是否可以被回收的呢?

引用計數演算法

引用計數演算法的原理是這樣的:給物件添加一個引用計數器, 每當有一個地方引用它時, 計數器值就加1;當引用失效時, 計數器值就減1;在任何時刻計數器的值為0的物件就是不可能再被使用的,

也就是可被回收的物件。

引用計數演算法的效率很高, 但是主流的JVM並沒有選用這種演算法來判定可回收物件, 因為它有一個致命的缺陷, 那就是它無法解決物件之間相互迴圈引用的的問題, 對於迴圈引用的物件它無法進行回收。

假設有這樣一段代碼:

public class Object { public Object instance; public static void main(String[] args) { // 1 Object objectA = new Object; Object objectB = new Object; // 2 objectA.instance = objectB; objectB.instance = objectA; // 3 objectA = null; objectB = null; }

程式啟動後, objectA和objectB兩個物件被創建並在堆中分配記憶體, 這兩個物件都相互持有對方的引用, 除此之外, 這兩個物件再無任何其他引用, 實際上這兩個物件已經不可能再被訪問(引用被置空, 無法訪問), 但是它們因為相互引用著對方, 導致它們的引用計數器都不為0, 於是引用計數演算法無法通知GC收集器回收它們。

實際上, 當第1步執行時, 兩個物件的引用計數器值都為1;當第2步執行時, 兩個物件的引用計數器都為2;當第3步執行時, 二者都清為空值, 引用計數器值都變為1。 根據引用計數演算法的思想, 值不為0的物件被認為是存活的, 不會被回收;而事實上這兩個物件已經不可能再被訪問了, 應該被回收。

可達性分析演算法

在主流的JVM實現中, 都是通過可達性分析演算法來判定物件是否存活的。 可達性分析演算法的基本思想是:通過一系列被稱為"GC Roots"的物件作為起始點, 從這些節點開始向下搜索, 搜索走過的路徑稱為引用鏈, 當一個物件到GC Roots物件沒有任何引用鏈相連, 就認為GC Roots到這個物件是不可達的, 判定此物件為不可用物件,

可以被回收。

在上圖中, objectA、objectB、objectC是可達的, 不會被回收;objectD、objectE雖然有關聯, 但是它們到GC Roots是不可達的, 所以它們將會被判定為是可回收的物件。

在Java中, 可作為GC Roots的對象包括下面幾種:

1、虛擬機器棧中引用的對象;

2、方法區中類靜態屬性引用的物件;

3、方法區中常量引用的物件;

4、本地方法棧中Native方法引用的物件。

以上探討了判定物件是否可回收的兩種演算法, 判定物件是否可回收只是垃圾回收的第一步, 接下來還要解決何時回收以及如何回收的問題, 在後面的文章中我們來探討這些問題。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示