1. 程式人生 > >Java虛擬機器判斷物件存活的兩種方案:引用計數法與可達性分析演算法

Java虛擬機器判斷物件存活的兩種方案:引用計數法與可達性分析演算法

java堆和方法區主要存放各種型別的物件(方法區中也儲存一些靜態變數和全域性常量等資訊),那麼我們在使用GC對其進行回收的時候首先要考慮的就是如何判斷一個物件是否應該被回收。也就是要判斷一個物件是否還有其他的引用或關聯使得這個物件處於存活的狀態。我們需要將不在存活狀態的所有物件標記出,以便於GC進行回收。

判斷物件是否存活有兩種比較常見的方法:引用計數法可達性分析演算法

引用計數法

引用計數法的邏輯非常簡單,但是存在問題,java並不採用這種方式進行物件存活判斷。

引用計數法的邏輯是:在堆中儲存物件時,在物件頭處維護一個counter計數器,如果一個物件增加了一個引用與之相連,則將counter++。如果一個引用關係失效則counter–。如果一個物件的counter變為0,則說明該物件已經被廢棄,不處於存活狀態。

這種方法來標記物件的狀態會存在很多問題:

1 jdk從1.2開始增加了多種引用方式:軟引用、弱引用、虛引用,且在不同引用情況下程式應進行不同的操作。如果我們只採用一個引用計數法來計數無法準確的區分這麼多種引用的情況。

引用計數法無法解決多種型別引用的問題。但這並不是致命的,因為我們可以通過增加邏輯區分四種引用情況,雖然麻煩一些但還算是引用計數法的變體,真正讓引用計數法徹底報廢的下面的情況。

2 如果一個物件A持有物件B,而物件B也持有一個物件A,那發生了類似作業系統中死鎖的迴圈持有,這種情況下A與B的counter恆大於1,會使得GC永遠無法回收這兩個物件。

可達性分析演算法

在主流的商用程式語言中(Java和C#),都是使用可達性分析演算法判斷物件是否存活的。這個演算法的基本思路就是通過一系列名為GC Roots的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件是不可用的,下圖物件object5, object6, object7雖然有互相判斷,但它們到GC Roots是不可達的,所以它們將會判定為是可回收物件。

這裡寫圖片描述

那麼那些點可以作為GC Roots呢?一般來說,如下情況的物件可以作為GC Roots:

  1. 虛擬機器棧(棧楨中的本地變量表)中的引用的物件
  2. 方法區中的類靜態屬性引用的物件
  3. 方法區中的常量引用的物件
  4. 本地方法棧中JNI(Native方法)的引用的物件