天天看點

JVM系列四(對象配置設定政策).

一、概要

前面的文章介紹了對象的建立過程,其中第三步 —— 配置設定記憶體,隻是簡單的介紹了配置設定的方式 —— 指針碰撞、空閑清單,其實對象在堆上配置設定還大有文章嘞。

對象的記憶體配置設定,往大方向上講,就是在堆上配置設定,對象主要配置設定在新生代的 Eden 區上,如果啟動了本地線程配置設定緩沖,将按線程優先在 TLAB 上配置設定。少數情況下也可能直接配置設定在老年代中,配置設定的規則并不是百分之百固定的。其細節取決于目前使用的是哪一種垃圾收集器的組合,還有虛拟機中與記憶體相關的參數的設定。

JVM系列四(對象配置設定政策).

下面介紹一些常見的記憶體配置設定政策。

二、對象優先在 Eden/TLAB 配置設定

虛拟機将新生代記憶體分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間(預設比例是 8:1:1),大多數情況下,配置設定對象時,使用 Eden 和其中一塊 Survivor 空間,當沒有足夠空間進行配置設定時,虛拟機将會進行一次 MinorGC。

如果虛拟機打開了 TLAB,那麼對象優先在 TLAB 上配置設定。TLAB 全稱是本地線程配置設定緩沖(Thread Local Allocation Buffer),它是每個線程在 Java 堆中預先配置設定的一小塊記憶體。因為 TLAB是線程私有的,沒有鎖開銷,是以性能較好,在 JDK7 之後預設開啟。

三、大對象直接進入老年代

虛拟機提供了一個 -XX:PretenureSizeThreshold 參數,令大于這個設定值的對象直接在老年代配置設定,這樣做的目的是避免在 Eden 區和及兩個 Survivor 區之間發生大量的記憶體複制。注意!該參數隻對 Serial 和 ParNew 收集器有效,Parallel Scavenge 并不認識該參數。

一般我們代碼中常見的大對象是指那種很長的字元串以及數組,寫程式的時候應當避免,經常出現大對象容易導緻記憶體還有不少空間時就提前觸發垃圾收集以擷取足夠的記憶體空間來“安置”它們。

四、長期存活的對象将進入老年代

還記得前面文章我們介紹對象建立的時候,說到對象頭中有一個 “GC 分代年齡” 嗎?那麼這個是做啥用的呢?

如果對象在 Eden 出生并經過第一次 Minor GC 後仍然存活,并且能被 Survivor 容納的話,将被移動到 Survivor 空間中,并且對象年齡設為1。對象在 Survivor 空間中每“熬過”一次 Minor GC,年齡就增加 1 歲,當它的年齡到達一定程度(最大為 15 歲),就将會被晉升到老年代。對象晉升老年代的年齡門檻值,可以通過參數 -XX:MaxTenuringThreshold 設定。

對象是否能夠晉升到老年代,也不全由 -XX:MaxTenuringThreshold 參數控制,如果 Survivor 空間中相同年齡的所有對象大小總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代。

五、空間配置設定擔保

新生代在發生 Minor GC 之前,虛拟機會先檢查老年代最大可用的連續空間是否大于新生代所有對象之和(或者曆次晉升老年代對象的平均大小)。如果這個條件不成立,那麼虛拟機将直接進行 Full GC 動作;如果這個條件成立,那麼虛拟機就會進行一次 Minor GC 操作,但是這次 Minor GC 是有風險的,因為比較的值是平均值,可能出現極端的情況 —— 大量對象在 Minor GC 後還存活,這時就隻好在失敗後重新發起一次 Full GC。