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

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

       在堆內存中存放着Java程序中幾乎全部的對象實例,堆內存的容量是有限的,Java虛擬機會對堆內存進行管理,回收已經「死去」的對象(即不可能再被任何途徑使用的對象),釋放內存。垃圾收集器在對堆內存進行回收前,首先要作的第一件事就是肯定這些對象中哪些還存活着,哪些已經死去。Java虛擬機是如何判斷對象是否能夠被回收的呢?學習

       引用計數算法spa

       引用計數算法的原理是這樣的:給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;在任什麼時候刻計數器的值爲0的對象就是不可能再被使用的,也就是可被回收的對象。code

       引用計數算法的效率很高,可是主流的JVM並無選用這種算法來斷定可回收對象,由於它有一個致命的缺陷,那就是它沒法解決對象之間相互循環引用的的問題,對於循環引用的對象它沒法進行回收。對象

       假設有這樣一段代碼:blog

       

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的對象被認爲是存活的,不會被回收;而事實上這兩個對象已經不可能再被訪問了,應該被回收。虛擬機

       可達性分析算法class

       在主流的JVM實現中,都是經過可達性分析算法來斷定對象是否存活的。可達性分析算法的基本思想是:經過一系列被稱爲"GC Roots"的對象做爲起始點,從這些節點開始向下搜索,搜索走過的路徑稱爲引用鏈,當一個對象到GC Roots對象沒有任何引用鏈相連,就認爲GC Roots到這個對象是不可達的,斷定此對象爲不可用對象,能夠被回收。效率

       

       在上圖中,objectA、objectB、objectC是可達的,不會被回收;objectD、objectE雖然有關聯,可是它們到GC Roots是不可達的,因此它們將會被斷定爲是可回收的對象。

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

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

       二、方法區中類靜態屬性引用的對象;

       三、方法區中常量引用的對象;

       四、本地方法棧中Native方法引用的對象。

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

相關文章
相關標籤/搜索