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


新生代和老年代

對象在被實例化之后,都是屬於新生代。

大部分新生代的生命周期都是及其短暫的,例如在一個方法中創建的對象會隨着方法執行完畢,棧空間的棧幀出棧后而失去引用。

而有一些對象確實會長期存活在堆內存的,比如被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)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM