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