1. 程式人生 > >深入理解Java虛擬機器筆記--記憶體分配規則

深入理解Java虛擬機器筆記--記憶體分配規則

記憶體分配規則

        通過在Client模式(也就是採用Serial/SerialOld收集器)下講解幾條最普遍的記憶體分配規則。

  • 物件優先在Eden區分配:在大多數情況下,物件都在Eden區進行分配。當Eden區沒有足夠的空間進行分配時將發起一次Minor GC。
  • 大物件直接進入老年代:所謂的大物件是指需要連續記憶體空間的Java物件,最典型的大物件就是那種很長的字串或者陣列。大物件對虛擬機器的記憶體分配來講是一個壞訊息(應該避免朝生夕死的大物件),經常出現大物件容易導致記憶體還有不少空間時就提前出發垃圾收集以獲取足夠的連續空間來“安置”它們。
  • 長期存活的物件將進入老年代:既然虛擬機器採用了分帶收集的思想來管理記憶體,那麼記憶體回收時就必須能夠識別哪些物件應該放在新生代,哪些物件應該放在老年代。為了能夠做到這一點,虛擬機器給每一個物件定義了一個物件年齡計數器。如果物件在Eden出生並經過第一次Minor GC 後仍然能夠存活,並且能夠被Survivor容納的話,將被移動到Survivor空間中,並且物件年齡設為1。物件在Survivor中每熬過一次Minor GC,年齡就增加一歲,當它的年齡增加到一定程度(預設是15歲)就會被晉升到老年代中。物件晉升老年代的年齡閥值可以通過引數-XX:MaxTenuringThreshold設定。
  • 動態物件年齡判斷。為了能夠更好地使用不同程式的記憶體狀況,虛擬機器並不是永遠地要求物件的年齡必須達到MaxTenuringThreshold才能晉升老年代,如果Survivor空間中相同年齡所有物件大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的物件就可以直接進入老年代,無需等到MaxTenuringThreshold中規定的年齡。

空間分配擔保

        在發生Minor GC之前,虛擬機器會先檢查老年代最大可用的連續記憶體空間是否大於新生代所有物件總空間,如果這個條件成立,那麼Minor GC可以確保是安全的。如果不成立,則虛擬機器會檢視HandlePromotionFailure設定值是否允許擔保失敗。如果允許,那麼會繼續檢查老年代最大可用的連續記憶體空間是否大於歷次晉升到老年代物件的平均大小,如果大於,將嘗試著進行一次Minor GC,儘管這次Minor GC是有風險的;如果小於,或者HandlePromotionFailure設定為不允許冒險,那麼這時也要改為進行一次Full GC。         下面解釋一下冒險是冒了什麼風險,新生代採用複製收集演算法,但為了記憶體利用率,只是用其中一個Survivor空間來作為轉換備份,因此當出現大量物件在Minor GC後仍然存活的情況,就需要老年代進行分配擔保,把Survivor無法容納的物件直接進入老年代。老年代要進行這樣的擔保,前提是老年代本身還有容納這些物件的剩餘空間,一共有多少物件會存活下來在完成記憶體回收之前是無法明確知道的,所以只好取之前每一次回收晉升老年代物件容量的平均大小值作為經驗值,與老年代的剩餘空間作比較,決定是否進行Full GC來讓老年代騰出更多記憶體。         取平均值進行比較仍然是一種動態概率的手段,也就是說,如果某次Minor GC後的物件激增,遠遠高於平均值的話,依然會導致擔保失敗。如果出現了Handler Promotion Failure,那麼只好在失敗後重新發起一次Full GC。雖然在擔保失敗後會繞圈子,但大多數情況下還是會將HandlePromotionFailure開關開啟,避免頻繁發生Full GC。