1. 程式人生 > >年輕代、老年代、GC原理詳細拆解

年輕代、老年代、GC原理詳細拆解

1、為什麼要對堆記憶體分代

     我們先來屢屢,為什麼需要把堆分代?不分代不能完成他所做的事情麼?其實不分代完全可以,分代的唯一理由就是優化GC效能。你先想想,如果沒有分代,那我們所有的物件都在一塊,GC的時候我們要找到哪些物件沒用,這樣就會對堆的所有區域進行掃描。而我們的很多物件都是朝生夕死的,如果分代的話,我們把新建立的物件放到某一地方,當GC的時候先把這塊存“朝生夕死”物件的區域進行回收,這樣就會騰出很大的空間出來。

2、年輕代

  HotSpot JVM把年輕代分為了三部分:1個Eden區和2個Survivor區(分別叫from和to)。預設比例為8:1,為啥預設會是這個比例,接下來我們會聊到。一般情況下,新建立的物件都會被分配到Eden區(一些大物件特殊處理),這些物件經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。物件在Survivor區中每熬過一次Minor GC(young GC),年齡就會增加1歲,當它的年齡增加到一定程度(15歲)時,就會被移動到年老代中。


    因為年輕代中的物件基本都是朝生夕死的(80%以上),所以在年輕代的垃圾回收演算法使用的是複製整理演算法,複製整理演算法的基本思想就是將記憶體分為兩塊,每次只用其中一塊,當這一塊記憶體用完,就將還活著的物件複製到另外一塊上面。

複製整理演算法

  • 優點:不會產生記憶體碎片。
  • 缺點:(1)會開闢新的空間也就是 To survivor,用來儲存有用物件(2)複製物件會花費一些時間

    在GC開始的時候,物件只會存在於Eden區和名為“From”的Survivor區,Survivor區“To”是空的。緊接著進行GC,Eden區中所有存活的物件都會被複制到“To”,而在“From”區中,仍存活的物件會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設定)的物件會被移動到年老代中,沒有達到閾值的物件會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名為To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有物件移動到年老代中。


3、老年代

    

    當年輕帶隨著不斷地Minor GC ,from survivor中的物件會不斷成長,當from survivor中的物件成長大15歲的時候,就會進入老年代,隨著Minor GC的持續進行,老年代中物件也會持續增長,最終老年代的空間也會不夠用,此時就會執行老年代的GC-->Major GC。Major GC使用的演算法是標記清除演算法或者標記-壓縮演算法。

    標記清除:(1)首先會去根物件的集合中進行遍歷,發現物件還存在引用,就是存活的物件並打上一個存活的標記(2)再去根物件集合進行二次遍歷,將沒有被打上標記的物件清除掉。(將所有物件掃描2次比較消耗時間)

        優點:能夠進入老年代的物件,一般都相對穩定,所有被回收的數量較少,減少對磁碟的清理,如果採用複製整理演算法,被複制的物件會有很多。

        缺點:雖然垃圾得到了回收,但是回收以後,堆記憶體中出現了不連續的現狀---記憶體碎片,導致大物件無法建立

     標記壓縮:和標記清除演算法基本相同,唯一不同的就是,在清除完成之後,會把存活的物件向記憶體的一邊進行壓縮,這樣就可以解決記憶體碎片問題。 

                  

4、有關年輕帶的配置引數

1)-XX:NewSize和-XX:MaxNewSize

   用於設定年輕代的大小,建議設為整個堆大小的1/3或者1/4,兩個值設為一樣大。

2)-XX:SurvivorRatio

   用於設定Eden和其中一個Survivor的比值,這個值也比較重要。

3)-XX:+PrintTenuringDistribution

   這個引數用於顯示每次Minor GC時Survivor區中各個年齡段的物件的大小。

4).-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold

   用於設定晉升到老年代的物件年齡的最小值和最大值,每個物件在堅持過一次Minor GC之後,年齡就加1。