1. 程式人生 > >Java虛擬機:如何判定哪些對象可回收?

Java虛擬機:如何判定哪些對象可回收?

都是 rgs 實例 分析算法 blog 分配內存 str 回收 new

版權聲明:本文為博主原創文章,轉載請註明出處,歡迎交流學習!

在堆內存中存放著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方法引用的對象。

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

Java虛擬機:如何判定哪些對象可回收?