Java虛擬機知識整理——內存分配

Java技術體系中鎖提倡的自動內存管理最終可以歸結爲自動化解決了兩個問題:給對象非配內存以及回收分配給對象的內存。回收內存就是垃圾回收機制。下面說一下內存分配。

對象有限在Eden分配

大多數情況下,對象在新生代Eden區中分配。當Eden去沒有足夠空間進行分配時,虛擬機將發起一次Minor GC
虛擬機提供-XX:+PrintGCDetails這個收集器日誌參數,告訴虛擬機在發生垃圾手機行爲時打印內存回收日誌,並且在進程退出的時候輸出當前內存各區域分配情況。

大對象直接進入老年代

所謂的大對象是指,需要大量連續內存空間的Java對象,最典型的大對象就是那種很長的字符串以及數組。大對象對虛擬機的內存分配來說就是一個壞消息(更壞的消息是遇到“朝生夕滅”的“短命大對象”,寫程序的時候要儘量避免),經常出現大對象容易導致內存還有不少空間時就提前出發垃圾收集以獲取足夠的連續空間來“安置”它們。
虛擬機提供-XX:+PretenureSizeThreshold參數,令大於這個設置值的對象直接在老年代分配。這樣做的目的是避免在Eden去及兩個Survuvor區之間發生大量的內存複製。

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

既然虛擬機曹勇了分代收集的思想來管理內存,那麼內存回收是就必須能識別哪些對象應放在新生代,那些對象應放在老年代中。爲了做到這點,虛擬機給每個對象定義了一個對象年齡計數器。如果對象在Eden出生並經過第一次MinorGC後仍然存活,並且能被Survivor容納的話,將被移動到Survivor空間中,並將對象年齡設爲1,對象在Survivor區中每“熬過”一次MinorGC年齡就增加一歲,當它的年齡增加到一定成都,就將會被晉升到老年代中。對象晉升老年帶的年齡閥值,可以通過參數-XX:MaxTenuringThreshold設置。

動態對象年齡判定

爲了能更好的適應不同程序的內存狀況,虛擬機並不是永遠地要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的綜合大於Survivor空間的一半,年齡大於或者等於該年齡的對象就可以直接進入老年代,無需等待MaxTenuringThreshold中要求的年齡。

空間分配擔保

在發生MinorGC之前,虛擬機會先檢查咯啊年代最大可用的連續空間是否大於新生代所有對象總空間,如果這個條件成立,那麼MinorGC可以確保是安全的。如果不成立,則虛擬機會查看HandlePromotionFailure設置值是否運行承擔失敗。如果允許,那麼會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年帶對象的平均大小,如果大於,將嘗試者進行一次MinorGC,儘管這次MinorGC是有風險的;如果小於,或者HandlePromotionFailure設置不允許冒險,那這時也要改爲進行一次FullGC。
下面解釋一下“冒險”是冒了什麼風險,前面提到過,新生代使用複製手機算大,但爲了提高內存利用率,只使用其中一個Survivor空間來作爲輪換備份,因此大概出現大量對象在MinorGC後仍然存活的情況(最極端的狀況就是內存回收後新生代中所有對象都存活)。就需要老年代進行分配擔保,把Survivor無法容納的對象直接進入老年代。與生活中的貸款擔保類似,老年代要進行這樣的擔保,前提是老年代本身還有容納這些對象的剩餘空間,一共有多少個對象會活下來在實際完成內存回收之前是無法明確知道的,所以知道取之前每一次回收晉升到老年代對象容量的平均大小值作爲經驗值,與老年代的剩餘空間進行比較決定是否進行FullGC來讓老年代騰出更多空間。
取平均值進行比較其實仍然是一種動態概率的手段,也就是說,如果某次MinorGC存活後的對象猛增,遠遠高於平均值的話,依然會導致擔保失敗,如果出現了HandlePromotionFailure石板,那就只好在失敗後重新發起一次FullGC。雖然擔保失敗時繞的圈子是最大的,但大部分情況下都還是會將HandlePromotionFailure開關打開,避免FullGC過於頻繁。

階段總結

內存回收與垃圾收集器在很多時候都是影響系統性能、併發能力的主要因素之一,虛擬機之所有提供多種不同的收集器以及提供大量的調節參數,是因爲只有根據實際應用需求、實現方式選擇最有的手機方式才能獲取最高的性能。沒有固定收集器、參數組合,也沒有最優的調優方法,虛擬機也就沒有什麼必然的內存回收行爲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章