java虛擬機器-記憶體分配與回收策略
大多數情況下,物件在新生代Eden區分配。當Eden區沒有足夠空間進行分配時,虛擬機器將發起一次Minor GC。
虛擬機器提供了-XX:+PrintGCDetails這個收集器日誌引數,告訴虛擬機器在傳送垃圾收集行為時列印記憶體回收日誌,並且在程序退出的時候輸出當前的記憶體區域分配情況。
2.大物件直接進入老年代
所謂的大物件是指,需要大量連續記憶體空間的java物件,最典型的大物件就是那種很長的字串以及陣列。大物件對虛擬機器的記憶體分配來說就是一個壞訊息(比遇到一個大物件更壞的訊息就是遇到一群“朝生夕滅”的“短命大物件”,寫程式的時候應該避免),經常出現大物件容易導致記憶體還有不少空間時就提前觸發垃圾收集以獲取足夠的連續空間來“安置”他們。
虛擬機器提供了一個-XX:PretenureSizeThreshold引數,令大於這個設定值的物件直接在老年代分配。這樣做的目的是避免在Eden區及兩個Survivor區之間發生大量的記憶體複製。
3.長期存活的物件將進入老年代
既然虛擬機器採用了分代收集的思想來管理記憶體,那麼記憶體回收時就必須能識別哪些物件應發在新生代,哪些物件應放在老年代。為了做到這一點,虛擬機器給每個物件定義了一個物件年齡(Age)計數器。如果物件在Eden出生並經過第一次Minor GC後仍然存活,並且能被Survivor容納的話,將被移動到Survivor空間中,並且物件年齡設為1.物件在Survivor區中每“熬過”一次Minor GC,年齡就增加一歲,當它的年齡增加到一定程度(預設15歲),就將會被晉升到老年代。物件晉升老年代的年齡閾值,可以通過引數-XX:MaxTenuringThreshold設定。
4.動態物件年齡判定
為了能更好的適應不同程式的記憶體狀況,虛擬機器並不是永遠的要求物件的年齡必須達到MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有物件大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的物件就可以直接進入老年代,無需等到MaxTenuringThreshold中要求的年齡。
5.空間分配擔保
在發生Minor GC之前,虛擬機器會先檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,如果這個條件成立,那麼Minor GC可以確保是安全的。如果不成立,則虛擬機器會檢視HandlePromotionFailure設定值是否允許擔保失敗。如果允許,那麼會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代物件的平均大小,如果大於,將嘗試進行一次Minor GC,儘管這次Minor GC是有風險的;如果小於或者HandlePromotionFailure設定為不允許冒險,那這時也要改為進行一次Full GC.