版權聲明:本文爲博主原創文章,轉載請註明出處,歡迎交流學習!算法
在堆內存中存放着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方法引用的對象。
以上探討了斷定對象是否可回收的兩種算法,斷定對象是否可回收只是垃圾回收的第一步,接下來還要解決什麼時候回收以及如何回收的問題,在後面的文章中咱們來探討這些問題。