1. 程式人生 > >GC roots如何判斷物件不可達

GC roots如何判斷物件不可達

查詢記憶體中不再使用的物件

引用計數法

引用計數法就是如果一個物件沒有被任何引用指向,則可視之為垃圾。這種方法的缺點就是不能檢測到環的存在。

public class MyObject {
    public Object ref = null;
    public static void main(String[] args) {
        MyObject myObject1 = new MyObject();
        MyObject myObject2 = new MyObject();
        myObject1.ref = myObject2;
        myObject2.ref
= myObject1; myObject1 = null; myObject2 = null; } }

這裡寫圖片描述
如果採用的是引用計數演算法:
再回到前面程式碼GcDemo的main方法共分為6個步驟:
Step1:GcObject例項1的引用計數加1,例項1的引用計數=1;
Step2:GcObject例項2的引用計數加1,例項2的引用計數=1;
Step3:GcObject例項2的引用計數再加1,例項2的引用計數=2;
Step4:GcObject例項1的引用計數再加1,例項1的引用計數=2;
執行到Step 4,則GcObject例項1和例項2的引用計數都等於2。
接下來繼續結果圖:
這裡寫圖片描述


Step5:棧幀中obj1不再指向Java堆,GcObject例項1的引用計數減1,結果為1;
Step6:棧幀中obj2不再指向Java堆,GcObject例項2的引用計數減1,結果為1。
到此,發現GcObject例項1和例項2的計數引用都不為0,那麼如果採用的引用計數演算法的話,那麼這兩個例項所佔的記憶體將得不到釋放,這便產生了記憶體洩露。

根搜尋演算法

這是目前主流的虛擬機器都是採用GC Roots Tracing演算法,比如Sun的Hotspot虛擬機器便是採用該演算法。 該演算法的核心演算法是從GC Roots物件作為起始點,利用數學中圖論知識,圖中可達物件便是存活物件,而不可達物件則是需要回收的垃圾記憶體

。這裡涉及兩個概念,一是GC Roots,一是可達性。

GCroots的節點:全域性性的引用(常量或靜態屬性)與執行上下文(例如棧幀中的區域性變量表中)
那麼可以作為GC Roots的物件(見下圖):

  • 虛擬機器棧的棧幀的區域性變量表所引用的物件;
  • 本地方法棧的JNI所引用的物件;
  • 方法區的靜態變數和常量所引用的物件;

關於可達性的物件,便是能與GC Roots構成連通圖的物件,如下圖:

這裡寫圖片描述

根搜尋演算法的基本思路就是通過一系列名為”GC Roots”的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain)當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件是不可用的。

從上圖,reference1、reference2、reference3都是GC Roots,可以看出:
reference1-> 物件例項1;
reference2-> 物件例項2;
reference3-> 物件例項4;
reference3-> 物件例項4 -> 物件例項6;
可以得出物件例項1、2、4、6都具有GC Roots可達性,也就是存活物件,不能被GC回收的物件。
而對於物件例項3、5直接雖然連通,但並沒有任何一個GC Roots與之相連,這便是GC Roots不可達的物件,這就是GC需要回收的垃圾物件。