1. 程式人生 > >關於JVM中Eden區、Survivor from區和Survivor to區的理解

關於JVM中Eden區、Survivor from區和Survivor to區的理解

本文主要根據《深入理解JVM》中記憶體回收策略,主要關注如下五個方面:

1:Eden區分配

2:大物件直接進入老年代

3:長期存活的物件直接進入老年代

4:動態物件年齡判定

5:空間分配擔保

首先明確新生代都是分配於Eden區的,所以Eden區是最重要也是記憶體回收最重要的管理區域,同時也是最頻繁的記憶體替換區域。我們知道JVM將記憶體根據分代策略將記憶體分為三層,新生代所佔據的記憶體、老年代所佔據的記憶體以及永久代,我們這裡不關注永久代,因為永久代是屬於方法區記憶體的部分,而新生代和老年代都是屬於堆記憶體區域的。

新生代中又繼續分為三個子塊,Eden區、Survivor from區、Survivor to區,實際上分為三個區的原因是為了方便採用複製-清除(詳情請參考深入理解JVM中記憶體回收策略)策略而採用的策略,複製策略就是將原來存在的記憶體分為兩個相等的區,使用一塊進行新生代的記憶體分配,當要GC時,則將存活的物件複製進入另一塊空閒的記憶體,然後將使用的記憶體進行清除,從而又有一個空閒區和一個使用區,並且不會有碎片問題。實際上並不需要兩個1:1的分割槽比例,因為一般存活的物件很少,所以JVM聰明的講新生代佔據的總記憶體分為Eden:Survivor from:Survivor to = 8:1:1三部分,其中Eden就用來分配新的物件記憶體,Survivor from則用於GC時的複製,那為什麼需要兩個Survivor區呢,因為複製後Survivor from區雖然現在很整齊,沒有碎片,當下一次進行回收時,Eden區和Survivor from區裡都存在需要回收的物件,則Survivor from區也會出現碎片。

那麼現在,我們看一下上述的五個部分:所有的新生代首先會在Eden區進行記憶體分配,當Eden區滿時會進行一次Minor GC操作,將Eden區進行回收,此時判斷存活的物件會被複制進入Survivor from區(年齡加1),對於大物件直接進入老年代,實際上是為了保證Eden區具有充足的空間可用的一種策略,採用-XX:PretenureSizeThreshold引數可以設定多大的物件可以直接進入老年代記憶體區域。對於長期存活的物件直接進入老年代,實際上時對Eden區到Survivor區過度的一種策略,是為了保證Eden區到Survivor區不會頻繁的進行復制一直存活的物件且對Survivor區也能保證不會具有太多的一直佔據的記憶體

,採用-XX:MaxTenuringThreshold=數字 引數可以設定物件在經過多少次GC後會被放入老年代(年齡達到設定值,預設為15)。對於動態物件年齡判斷,實際上是對Survivor區的一種策略,是為了保證Survivor區具有充足的空間用於分配,動態物件年齡只判斷Survivor區是否存在相等物件年齡的物件是否超過Survivor from/to的一半時,直接將超過的物件放入老年代。對於空間分配擔保實際上是針對老年代,為了保證老年代的記憶體區域具有充足的空間,不至於記憶體溢位的情況出現,在發生MinorGC之前,JVM會判斷之前每次晉升到老年代的平均大小是否大於老年代剩餘空間的大小,若大於則進行full GC(即回收所有區域),若小於,則還需要檢視一個引數HandlePromotionFailure,即是否允許擔保失敗,因為實際上進入老年代的物件大小在GC前是未知的,這也是為什麼採用之前晉升的平均值來進行判斷擔保,也就是說只是一種預測,並不能代表真實就是有這麼多物件晉升,所以若不允許擔保失敗,即保守的人為一定會有超過剩餘老年代區域的物件存入,則還是進行Full GC,否則,進行Minor GC。