1. 程式人生 > >新生代和老年代垃圾回收的細節

新生代和老年代垃圾回收的細節

新生代和老年代

物件在被例項化之後,都是屬於新生代。

大部分新生代的生命週期都是及其短暫的,例如在一個方法中建立的物件會隨著方法執行完畢,棧空間的棧幀出棧後而失去引用。

而有一些物件確實會長期存活在堆記憶體的,比如被Static引用的物件。這種物件不會輕易的被垃圾回收器回收。

所以JVM會將堆記憶體分為兩個區域,一個年輕代,一個老年代。

其中年輕代,顧名思義,就是建立和使用完之後立馬就要被回收的物件放在裡面。然後老年帶呢,就是把一些會長期存活的物件放在裡面。

為什麼要分成新生代和老年代

很多人都會有一個疑問,為什麼要劃分兩個區域呢?

因為這個垃圾回收有關,對於年輕代的物件,他們的特點是很快就會被回收,所以需要使用一種垃圾回收演算法。

而對於老年代而言,裡面的大部分物件可能都會長期存活,那麼使用新生代的回收演算法放在這裡就可能並不是那麼的合適。需要有著自己的一套回收演算法。

什麼是永久代

很簡單,JVM裡的永久代其實就是我們之前說的方法區

所謂的永久代,你可以認為是存放一些類的資訊,在上一個章節我們知道我們生成的.class就是存放在這個區域的。一般情況下,我們對於jvm調優都是對新生代和老年代進行調優。一般而言永久代保持預設配置就可以了。

是不是意味著我們就不需要關注永久代?

肯定不是的。因為要儲存類的相關資訊,所以對於動態生成類的情況比較容易出現永久代的記憶體溢位。最典型的場景就是,在 jsp 頁面比較多的情況,容易出現永久代記憶體溢位。

如何判斷一個物件是否是垃圾

我們知道,當一個物件被創建出來的時候,比如說在一個方法中建立一個物件,當該方法執行完畢後,就沒有引用指向這個物件了,這個物件就會變成垃圾物件。

這僅僅是一種情況。

到低哪些物件是辣雞,哪些物件不是?

JVM中使用了一種可達性分析演算法來判定哪些物件是可以被回收的。這個演算法的核心就是看這個物件有誰在引用它,然後一層一層的往上判斷,看是否被GC roots所引用。

在java中,可作為GC Roots的物件有:

1.虛擬機器棧(棧幀中的本地變量表)中引用的物件(也就是我們前面提到在方法中建立的物件);
2.方法區中的類靜態屬性引用的物件;
3.方法區中常量引用的物件;
4.本地方法棧中JNI(即一般說的Native方法)中引用的物件

當一個物件沒有被上述所引用,那麼這個物件就可以被認為是垃圾物件了。

總之記住一句話,只要你的物件被方法的具備變數,類的靜態變數給引用了,就不會回收他。

java中物件不同的引用關係。

1、強引用

就是被GC roots所直接引用的物件。只要是強引用關係,那麼垃圾回收器是絕對不會回收這個物件的。

2、軟引用

public class test{
    public static SoftReference<ReplicaManager> manager =
        new SoftReference<ReplicaManager>(new Replicamanager)
}

/**如上訴程式碼,例項物件被“SoftReference” 軟引用型別的物件包裝起來了,那麼這個物件的引用就是軟引用。

正常情況下是不會回收軟引用物件的,但是如果你進行垃圾回收後,返現記憶體還是不夠存放新的物件的時候,這個時候就會吧軟引用的物件給回收掉
**/

3、弱引用

這個相對前兩中使用的很少,與軟引用類時,當一個例項物件被“WeakReference”弱引用的物件包裝起來的時候,那麼這個物件就是弱引用。
弱引用的生命週期就存在下一次垃圾回收之前,也就是說下一次垃圾回收會回收掉弱引用的物件。

4、虛引用

最弱雞的一種引用,我感覺沒什麼作用。一個物件是不是虛引用對他本生的生命週期沒有影響。該什麼時候回收就什麼時候回收。
設定他唯一的目的就是被回收的時候會得到一個系統通知,一般來說沒什麼卵用

finalize()方法的作用

到這裡,我相信你應該清楚了哪些物件會被回收哪些物件不會被回收。

如果一個物件被GC Roots所引用,但是!!他如果是軟引用或弱引用,那麼也是可能會被辣雞回收器給回收掉的。

在垃圾物件被回收的時候,會呼叫Object物件的finalize()方法。

我們來模擬一個辣雞的自我救贖程式碼。

public class A{
    public static A instance;
    @Override
    protected void finalize() throws Throwable{
        A.instance = this;
    }
}

當物件被回收的時候,呼叫finalize方法,重新將GC Roots的變數引用指向自己,那麼就不會被回收了。

這個基本上沒人用,感覺就是一個sb才會寫的程式碼。寫出來就是給大家說出這個細節。

新生代物件是如何變成老年代物件的

長期存活的物件會多次躲過垃圾回收

躲過多少次Minor GC而沒有被回收掉,我們就認為這個物件的年齡有幾歲了,預設情況下,當一個物件10多歲的時候,就認為他是一個老人了。需要被轉移到老年代去。

想想也正常,新生代的競爭壓力這麼大,老年物件還是早點去老年代比較好不與年輕人競爭資源。

特別大的超大物件直接不經過新生代就進入老年代

年輕代的的競爭本來就很大,你還要佔用那麼多資源,不行不行,你還是去老年代吧。

動態年齡判斷機制

這一部分需要結合垃圾回收器的演算法來講,就是複製演算法。我們在後面的章節會講到,這裡大概介紹一下。

我們一般會將新生代分為三個區域,一個Eden,兩個Survivor。比例8:1:1.

生成的物件預設在eden區域。當發生一次Minor GC後,會將存活的物件複製到其中一個Survivor區域。當下一次GC後又會將存活的物件複製到另一塊Survivor。這麼做的好處是減少記憶體碎片。

當我們發生一次GC後,將存活物件放到其中一塊survivor區域。發現其中的1歲,2歲,3歲的物件年齡加起來記憶體超過survivor區域的一半,就會把4歲以及4歲以上的對想轉移到老年代。

GC後survivor區域存放不下

這個沒辦法,GC後都還存放不下一般來說要不是訪問量激增,要麼就是優化的不到位,所以只好將這寫物件轉移到老年代。

空間擔保機制

在發生minor gc之前,虛擬機器會檢測 : 老年代最大可用的連續空間>新生代all物件總空間?

1、滿足,minor gc是安全的,可以進行minor gc。

2、不滿足,虛擬機器檢視HandlePromotionFailure引數:

(1)為true,允許擔保失敗,會繼續檢測老年代最大可用的連續空間>歷次晉升到老年代物件的平均大小。若大於,將嘗試進行一次minor gc,若失敗,則重新進行一次full gc。

(2)為false,則不允許冒險,要進行full gc(對老年代進行gc)