對象的內(nèi)存分配有套路?
前言
Java技術(shù)體系中所提倡的自動內(nèi)存管理最終可以歸結(jié)為自動化地解決了兩個問題:給對象分配內(nèi)存以及回收分配給對象的內(nèi)存。
回收對象內(nèi)存是垃圾收集器的工作,在上一篇文章中已有闡述,這篇文章主要說一下對象的內(nèi)存分配以及回收策略。
本文大綱:
- 1、對象優(yōu)先分配在Eden區(qū)
- 2、大對象直接進入老年代
- 3、長期存活的對象將進入老年代
- 4、動態(tài)對象年齡判定
- 5、空間分配擔(dān)保
- 6、總結(jié)
一、對象優(yōu)先分配在Eden區(qū)
大多數(shù)情況下,對象都是優(yōu)先在Eden區(qū)分配的,當(dāng)Eden區(qū)沒有足夠的空間進行分配時,則虛擬機會進行GC回收(Minor GC)。
設(shè)置***堆和初始化堆都為20M,新生代分配10M,打印GC軌跡。
運行結(jié)果如下:
可以發(fā)現(xiàn)對象都被分配在Eden區(qū),默認的Eden區(qū)與Survivor區(qū)比例是8,所以Eden區(qū)占8/10=8M,Survivor區(qū)有兩個,每個都是1M。Eden區(qū)使用了56%,即5.6M,程序中對象obj和obj2各占2M,那多出來的1.6M是哪里來的?
原因是程序中的對象被存儲時會被轉(zhuǎn)換為虛擬機對象,而虛擬機對象包括對象頭、對象的實例數(shù)據(jù)以及對齊填充。對象的實例數(shù)據(jù)可以理解為我們程序中分配的2M,多出來的1.6M自然就是對象頭和對齊填充搞的事。
二、大對象直接進入老年代
大對象會被分配進老年代,可以通過虛擬機參數(shù)PretenureSizeThreshold來指定多大才算大對象。
設(shè)置***堆和初始化堆都為20M,新生代分配10M,打印GC軌跡,3M視為大對象。
運行結(jié)果如下,可以發(fā)現(xiàn)6M的對象被分配到了老年代(tenured generation)中。
三、長期存活的對象將進入老年代
長期存活的對象也會被晉升到老年代中,默認是15次的Minor GC年齡。意思就是一個對象在新生代中發(fā)生了15次的GC之后,如果還存活就會晉升為老年代對象。
這個年齡可以通過虛擬機參數(shù)MaxTenuringThreshold進行配置。
設(shè)置MaxTenuringThreshold=0即意味著只要新生代發(fā)現(xiàn)GC馬上晉升為老年代對象。
運行結(jié)果如下,發(fā)現(xiàn)在***次GC的時候,對象obj和obj2都進入了老年代。
設(shè)置MaxTenuringThreshold=3即意味著要經(jīng)過三次GC才可以晉升為老年代對象。
運行結(jié)果如下,發(fā)現(xiàn)這次只有obj2進入了老年代,對象obj2是因為太大在Survivor區(qū)存不下才進入老年代的。毫無懸念,對象obj留在了Survivor區(qū)。Eden存的是對象obj3。
四、動態(tài)對象年齡判定
為了更好地適應(yīng)不同程序的內(nèi)存狀況,虛擬機并不是永遠要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象的大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代。
首先為對象obj、obj2各分配256K內(nèi)存,他們之和大于512K(因為虛擬機對象還包含對象頭,所以是大于,不是等于),即大于Survivor的一半,所以會晉升為老年代。
運行結(jié)果如下,可以發(fā)現(xiàn)對象obj、obj2、obj3都進入老年代。對象obj3是因為太大Survivor存不下而進入老年代的。
為了更好的體驗動態(tài)年齡的效果,作一個對比,這次設(shè)置為對象obj、obj2各分配128K內(nèi)存,他們之和小于512K,即小于Survivor的一半,所以不會晉升為老年代。
運行結(jié)果如下,可以發(fā)現(xiàn)對象obj、obj2被存儲于survivor區(qū)了。老年代存儲的是對象obj3,Eden區(qū)存儲的是***壓入內(nèi)存的obj4對象。
五、空間分配擔(dān)保
在發(fā)生MinorGC之前,虛擬機會先檢查老年代***可用的連續(xù)空間是否大于新生代所有對象的總空間,如果這個條件成立,即大于,那么MinorGC可以確保是安全的。當(dāng)不大于時會有空間分配擔(dān)保一說法。
空間分配擔(dān)保是指上面的條件不成立時,如果允許空間分配擔(dān)保,則虛擬機會進行一次MinorGC,而不是Full GC,盡管有可能內(nèi)存溢出。如果不允許空間分配擔(dān)保,則會進行一次FullGC,那停頓的時間就相對長很多了。一般FullGC的停頓時間是Minor GC的十倍。補充一點,是否允許空間分配擔(dān)保可以通過虛擬機參數(shù)HandlePromotionFailure配置。
簡而言之,投資總有風(fēng)險,只不過空間分配擔(dān)保的回報率很高,可以減少停頓時間,提高應(yīng)用程序的效應(yīng)速度。



































