以下為對象在內存中分配的規則,這些規則并不是百分之百固定的,其細節取決于正在使用的垃圾收集策略,當然還有與虛擬機的內存相關參數設置有關。
1. 對象優先在Eden分配
一般,對象在新生代Eden區中分配,當Eden區沒有足夠空間進行分配時(一般不會將新生對象直接放入Survivor中,它只保存上一次GC還存活的對象),虛擬機將發生一次Minor GC(當Eden空間不足,就會發生對新生代的GC)。
2. 大對象直接進入老年代
所謂的大對象是指,需要大量連續內存空間的Java對象,最經典的大對象就是那種很長的字符串以及數組。經常出現大對象容易導致內存還有不少空間時就提前出發垃圾收集。所以應當盡量避免短命大對象的使用。
3. 長期存活的對象將進入老年代
對象的年齡:虛擬機給每個對象定義了一個對象年齡計數器,如果對象在Eden出生并經過第一次的MinorGC后仍然存活,并且能被Survivor區中每“熬過”一次Minor GC,年齡就增加一歲,當它的年齡增加到一定程度(默認為15歲),就會被晉升到老年代中。
4. 動態對象年齡判定
為了更好的適應不同程度的內存狀況,虛擬機并不是永遠地要求對象的年齡必須達到閥值才能晉升老年代,如果Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到閥值中要求的年齡。
5. 空間分配擔保
- 在發生MinorGC之前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有對象的總空間,如果這個條件成立,那么Minor GC 可以確保是安全的。
- 如果不成立,則虛擬機會查看HandlePromotionFailure設置值是否允許擔保失敗。如果允許,那么會繼續查找老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試著進行一次Minor GC,盡管這次MinorGC是有風險的
- 如果小于,或者HandlePromotionFailure設置不允許冒險,那這時也要改為進行一次Full GC。
觸發Full GC的五種情況
- 直接調用System.gc()
- 老年代空間不足
- 永久代空間滿
Java 8 中已經移除了永久代,改用元空間。元空間的本質和永久代類似,都是對JVM規范中方法區的實現。不過元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地內存。因此,默認情況下,元空間的大小僅受本地內存限制。
Minor GC 會清理新生代(滿或者無法分配大對象),Major GC是清理永久代。Full GC是清理整個堆空間——包括新生代和老年代。
- 在使用CMS策略對老年代進行垃圾回收時
- promotion failed: 進行Minor GC時,Survivor 放不下上一次存活的對象,對象只能放入老年代,而此時老年代也放不下造成的。
- concurrent mode failure:在執行CMS GC的過程中同時有對象要放入老年代,而此時老年代空間不足造成的。
- 統計得到的Minor GC 晉升到老年代的平均大小大于老年代的剩余空間
HotSpot 為了避免由于新生代對象晉升到老年代導致老年代空間不足的現象,在進行Minor GC時,做了有一個判斷,如果之前統計所得的MInor GC 晉升到老年代的平均大小大于老年代的剩余空間,那么就直接觸發 Full GC。