內(nèi)存分配與回收策略

聲明:本文摘抄自《深入理解Java虛擬機》一書,本文完全為自我學習,請感興趣的同學購買正版,支持原創(chuàng)

Java內(nèi)存管理主要解決兩個問題:給對象分配內(nèi)存和回收分配給對象的內(nèi)存。

對象內(nèi)存分配,從大的方向來將,就是在堆上分配,對象主要分配在新生代的Eden空間,如果啟用了本地線程分配緩沖,將按線程優(yōu)先級在TLAB上分配。少數(shù)情況下也會直接分配到老年代中,分配的規(guī)則并不是百分百固定的,其細節(jié)取決于采用哪種垃圾收集器組合,還有虛擬機與內(nèi)存相關(guān)的參設置。

對象優(yōu)先在Eden分配

大多數(shù)情況下,對象在新生代Eden空間分配,當Eden沒有足夠空間進行分配時,虛擬機將發(fā)起一次Minor GC。
虛擬機提供了-XX:+PrintGCDetails這個收集器日志參數(shù),告訴虛擬機在發(fā)生垃圾收集行為時打印內(nèi)存回收日志,并且在進程退出時輸出當前內(nèi)存各個區(qū)域的分配情況。

注意

  • 新生代GC(Minor GC): 指發(fā)生在新生代的垃圾收集動作,因為Java對象大多具備朝生夕死的特性,所以Minor GC會比較頻繁,一般回收速度也比較快。
  • 老年代GC(Major GC / Full GC):指發(fā)生在老年代的GC,出現(xiàn)了Major GC,經(jīng)常會伴隨著一次或多次Minor GC。Major GC的速度一般會比Minor GC慢10倍以上。
大對象直接進入老年代

所謂的大對象是指,需要大量連續(xù)內(nèi)存空間的Java對象,最典型的大對象就是很長的字符串或數(shù)組。大對象的分配對虛擬機來說是一個壞消息,經(jīng)常出現(xiàn)大對象容易導致內(nèi)存還有不少空間時就提前觸發(fā)垃圾收集以獲得足夠的連續(xù)空間來”安置“他們。
虛擬機提供了一個-XX:PretenureSizeThreshold參數(shù),令大于這個設置值的對象直接在老年代分配。這樣做的目的是避免在Eden空間以及兩個Survivor空間發(fā)生大量的內(nèi)存復制。

長期存活的對象直接進入老年代

既然虛擬機采用分代收集的思想來管理內(nèi)存,那么內(nèi)存回收就必須識別哪些對象應該放在新生代,哪些對象應該放入老年代。為了做到這一點,虛擬機給每個對象定義了一個對象年齡(Age)的計數(shù)器。如果對象在Eden空間出生并經(jīng)過第一次Minor GC后仍然存活,并且能夠被Survivor容納的話,將被移動到Survivor空間,并且對象年齡設置為1。對象在Survivor空間每”熬過“一次Minor GC,年齡就增加1歲,當年齡增加到一定程度(默認為15歲),就將會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數(shù)-XX:MaxTenuringThreshold設置。

動態(tài)對象年齡判定

為了能更好地適應不同程序的內(nèi)存狀況,虛擬機并不是永遠地要求對象的年齡必須達到了MaxTenuringThreshod才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到MaxTenuringThreshod要求的年齡。

空間分配擔保

在發(fā)生Minor GC前,虛擬機會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間,如果這個條件成立,那么Minor GC可以確保是安全的。如果不成立,則虛擬機會查看-XX:HandlePromotionFailure設置值是否允許擔保失敗。如果允許,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均水平,如果大于,將嘗試進行一次Minor GC,盡管這次Minor GC是有風險的;如果小于,或者-XX:HandlePromotionFailure設置不允許冒險,那這時要改為進行一次Full GC。
取平均值進行比較其實仍然是一種動態(tài)概率的手段,也就是說,如果某次Minor GC后,存活對象突增,遠高于平均值的話,依然會出現(xiàn)擔保失?。℉andle Promotion Failure)。如果出現(xiàn)擔保失敗,那就只好在失敗后重新發(fā)起一次Full GC。雖然擔保失敗時繞的圈子是最大的,但大部分情況下都還是將擔保失敗的開關(guān)打開,避免Full GC過于頻繁。
在JDK6 Update24之后,雖然JVM源碼中還定義了HandlePromotionFailure參數(shù),但是在代碼中已經(jīng)不會再使用它。JDK6 Update24之后的規(guī)則變?yōu)橹灰夏甏倪B續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小就會進行Minor GC,否則將進行Full GC。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容