重識JVM(六):內存分配原則

對象的內存分配,就是在堆上分配,對象主要分配在新生代的Eden 區上,如果啓動了本地線程分配緩衝,將按線程優先在TLAB 上分配少數情況下也可能直接分配在老年代中,分配的規則不是固定的,其細節取決於當前使用的是哪一種垃圾收集器組合,還有虛擬機中與內存相關的參數設置。
下面會講解幾條最普遍的內存分配原則。
對象優先在Eden 分配
大多數情況下,對象在新生代Eden 區中分配。當Eden 區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。如果GC 期間虛擬機發現已有的對象全部無法放入Survivor 空間,會通過分配擔保機制提前轉移至老年代中。
大對象直接進入老年代
所謂的大對象是指,需要大量連續內存空間的Java 對象,最典型的大對象就是那種很長的字符串以及數組。經常出現大對象容易導致內存還有不少空間就提前觸發垃圾收集以獲取足夠的連續空間來安置它們。
長期存活的對象將進入老年代
虛擬機爲每個對象定義一個對象年齡(Age)計數器。如果對象在Eden 出生並經過第一次Minor GC 後仍然存活,並且能夠被Survivor 容納的話,將被移動到Survivor 空間中,並且對象年齡設爲1.對象在Survivor 區中每熬過一次Minor GC,年齡就增加1 歲,當它的年齡增加到一定程度(默認爲15 歲),就將會被晉升到老年代。
動態對象年齡判定
虛擬機並不是永遠地要求對象的年齡必須達到MaxTenuringThreshold 才能晉升老年代,如果在Survivor 空間中相同年齡所有對象大小的總和大於Survivor 空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代。
空間分配擔保
在發生Minor GC 之前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代所有對象總空間,如果這個條件成立,那麼Minor GC 可以確保是安全的。如果不成立,則虛擬機會查看HandlePromotionFailure 設置值是否允許擔保失敗。如果允許,那麼會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小,如果大於,將嘗試着進行一次Minor GC,儘管這次Minor GC 是有風險的;如果小於,或者HandlePromotionFailure設置不允許冒險,那此時也要改爲進行一次Full GC。
冒險是指當出現大量對象在Minor GC 後仍然存活的情況,就需要老年代進行分配擔保,把Survivor 區無法容納的對象直接進入老年代。老年代要進行這樣的擔保,前提是老年代本身還有容納這些對象的剩餘空間,一共會有多少對象會活下來在實際完成內存回收之前是無法明確知道的,所以只好取之間每一次回收晉升到老年代對象容量的平均大小值作爲經驗值,與老年代的剩餘空間進行比較,決定是否進行Full GC 來讓老年代騰出更多空間。取平均值進行比較其實仍然是一種動態概率的手段,依然存在擔保失敗的情況。如果出現了HandlePromotionFailure 失敗,那就只好在失敗後重新發起一次Full GC。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章