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