背景
說到java的特性,其中一個最重要的特性便是java通過new在堆中分配給對象的內存,不需要程序員主動去釋放,而是由java虛擬機自動的回收。這也是java和C++的主要區別之一;那么虛擬機是如何實現自動回收的呢?它的基本回收算法又是什么呢? 這篇隨筆先不介紹這些~ ~,熟話說 飯要一口一口地吃,路要一步一步地走嘛,這篇隨筆主要講解的是回收的前提:如何判斷一個對象可以回收。
對java中如何判斷一個對象可以回收的一般性認識
在沒有學習《深入理解java虛擬機》之前,對於java中判斷一個對象是否可以回收的方法,我自然而然的就想到了通過引用計數的方法就可以做到,我想的是:對象自身有一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時(例如程序離開了引用對象它所在的作用域),計數器值減1;任何時刻計數器為零的對象就是不會再被使用的,內存自動回收時就可以對它進行回收了;
這種方式又簡單,又高效,那為什么主流的java虛擬機沒有采用這種方式呢,其中主要的原因是這種方式很難解決對象之間相互循環引用的問題;看下面的簡單代碼例子:
public class Main {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
//讓對象占用一定的內存空間,觸發回收
private byte[] bigSize = new byte[2 * _1MB];
public static void testGC(){
Main objA = new Main();
Main objB = new Main();
//objA中的instance對象引用objB,objB中的instance對象引用objA
objA.instance = objB;
objB.instance = objA;
//將兩個對象都設為null
objA = null;
objB = null;
System.gc();
}
}
對於上面的情況,如果java虛擬機采用引用計數的方式進行判斷對象是否可以回收,那么objA,和 objB是永遠也得不到回收的!
回到題目,那么java虛擬機采用什么方法來進行判斷一個對象是否可以被回收呢?
可達性分析算法:
正如算法名稱說描述的,這個算法的基本思想是:通過一系列稱為“GC Root” 的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Root 沒有任何引用鏈相連接(用圖論來說就是從根節點到這個對象節點不可達)時,則證明這個對象是不可用的;如圖1-1 所示,對象 5, 6, 7雖然互相有關聯,但是它們到GC Root 是不可達的,所以它們將被判定為可回收對象,即這幾個對象可以被判定已死亡;
如圖 1-1:可達性分析算法判定對象是否可回收
看了上圖以后,又會有一個問題,就是如何選取GC Roots:
包括以下下幾種對象:
1.虛擬機棧中引用的對象(就是java函數體中的本地變量表)。
2.方法區中類靜態屬性引用的對象。
3.方法區中常量引用的對象。
4.本地方法棧中JNI(即一般說的Native方法)引用的對象。
以上就是我對java內存自動回收部分知識點的一個總結和分享,其中還有很多知識點需要去學習,比如方法區的存儲結構是怎樣的,存儲哪些類型數據信息,虛擬機如何管理這一塊內存區域等。
希望這次分享能對大家有所啟發,有所收獲~