記憶體分配策略學習

hatch發表於2018-04-15

物件的記憶體分配,基本上是在堆上分配的(但也可能經過JIT編譯後被拆散為標量型別間接的棧上分配),物件主要分配子啊新生代的Eden區,若啟動了本地執行緒分配快取,會按執行緒優先在TLAB上分配;當然少數也可能直接分配在老年代。這都取決於當前使用的哪一種垃圾收集器的組合,還有相關引數配置。

記憶體分配策略學習

物件優先在Eden分配

大多數情況下,物件在新生代Eden區中分配。當Eden中沒有足夠空間分配時,將觸發一次Minor GC。

大物件直接進入老年代

大物件是指需要大量連續記憶體空間的Java物件。

因為新生代採用複製演算法收集記憶體,因此大物件的分配對於JVM是很苦惱的,尤其是當這些大物件很短命的時候,經常出現大物件容易導致記憶體還有不少空間時就提前出發垃圾收集以獲取足夠的連續空間來安置他們。

虛擬機器提供一個-XX:PretensureSizeThreshold引數設定大於這個配置值得物件直接在老年代分配。可以避免Eden區和兩個Survivor區之間發生大量的記憶體複製。

長期存活的物件將進入老年代

虛擬機器為每個物件定義一個物件年齡計數器;

物件在Eden出生並經過第一次Minor GC後仍活著的話,並且能被Survivor容納的,將會被移至Survivor區,且年齡為1;

物件在Suvivor中每熬過一次Minor GC,年齡就長1歲;

當年齡長到一定程度(預設15歲)將會進入老年代。

可以設定-XX:MaxTenuringThreshold 物件進入老年代的年齡閾值

動態物件年齡判斷

為了更好的適應不同程式的記憶體狀況,虛擬機器不會固定要求物件必須達到什麼年齡才進入老年代。

如果Suvivor中相同年齡的物件大小大於Survivor空間的一半,那麼年齡大於等於該年齡的物件都會直接進入老年代。

空間分配擔保

新生代採用複製收集演算法,但是為了記憶體利用率,只是用一個Survivor作為輪換備份,Minor GC後最極端的情況是新生代的所有物件都存活,這時Eden區的物件要進入Survivor區中,老年代要進行分配擔保來把Survivor無法容納的物件直接進入老年代。

因為經過Minor GC後到底有多少物件存活在完成記憶體回收之前是未知的,因此取之前每次晉升到老年代物件的容量的平均大小作為經驗值與老年代剩餘空間進行比較來決定是否進行Full GC來讓老年代騰出更多空間。

如果某次Minor GC存活後的物件突增,遠遠高於平均值的話,依然會導致擔保失敗(Handle Promotion Failure)。如果出現了HandlePromotionFailure失敗,那就只能失敗後重新發起一次Full GC

另外在JDK 6 Update 24之後,虛擬機器已經不再使用HandlePromotionFailure引數了,規則變為只要老年代的連續空間大於新生代物件總大小或者歷次晉升的平均大小就會進行Minor GC,否則將進行Full GC。

相關文章