對象內存分配過程

對象內存分配流程圖

image

3.1 棧上分配

Java對象都是在堆上進行分配,在對象沒有被引用時,依賴GC回收內存。當對象數量過多,便會給GC帶了較大壓力,影響應用性能。JVM提供了棧上分配機制,用於減少臨時對象在堆上的分配數量。

JVM通過逃逸分析確定該對象不會被外部訪問。如果不會逃逸,可以優化對象內存分配位置,通過標量替換優先分配在棧上(棧上分配),這樣該對象所佔用的內存空間就可以隨棧幀出棧而銷燬,就減輕了垃圾回收的壓力。`

對象逃逸分析:就是分析對象動態作用域。JDK7之後默認開啓逃逸分析-XX:+DoEscapeAnalysis

標量替換:通過逃逸分析確定該對象不會被外部訪問,並且對象可以被進一步分解時,JVM不會創建該對象,而是將該對象成員變量分解若干個被這個方法使用的成員變量所代替,這些代替的成員變量在棧幀或寄存器上分配空間,這樣就不會因爲沒有一大塊連續空間導致對象內存不夠分配。開啓標量替換參數(-XX:+EliminateAllocations),JDK7之後默認開啓

結論:棧上分配依賴於逃逸分析和標量替換

// -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
public class AllotOnStack {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    private static void alloc() {
        User user = new User();
        user.setId(1);
        user.setName("zhuge");
    }
}

3.2 對象在Eden區分配

大多數情況下,對象在新生代中 Eden 區分配。雖然可能經過TLAB,但TLAB也是Eden區域。當 Eden 區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。

  • Minor GC/Young GC:指發生新生代的的垃圾收集動作,Minor GC非常頻繁,回收速度一般也比較快。
  • Major GC/Full GC:一般會回收老年代 ,年輕代,方法區的垃圾,Major GC的速度一般會比Minor GC的慢10倍以上。

Eden與Survivor區默認8:1:1

大量的對象被分配在eden區,eden區滿了後會觸發minor gc,可能會有99%以上的對象成爲垃圾被回收掉,剩餘存活的對象會被挪到爲空的那塊survivor區,下一次eden區滿了後又會觸發minor gc,把eden區和survivor區垃圾對象回收,把剩餘存活的對象一次性挪動到另外一塊爲空的survivor區,因爲新生代的對象都是朝生夕死的,存活時間很短,所以JVM默認的8:1:1的比例是很合適的,讓eden區儘量的大,survivor區夠用即可,

JVM默認有這個參數-XX:+UseAdaptiveSizePolicy(默認開啓),會導致這個8:1:1比例自動變化。

3.3 大對象直接進入老年代

大對象就是需要大量連續內存空間的對象(比如:字符串、數組)。JVM參數 -XX:PretenureSizeThreshold 可以設置大對象的大小,如果對象超過設置大小會直接進入老年代,不會進入年輕代,這個參數只在 Serial 和ParNew兩個收集器下有效。

比如設置JVM參數:-XX:PretenureSizeThreshold=1000000 (單位是字節) -XX:+UseSerialGC ,再執行下上面的第一個程序會發現大對象直接進了老年代

爲什麼要這樣呢?

爲了避免GC時,大對象在Eden區以及兩個Survior區多次複製操作。

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

既然虛擬機採用了分代收集的思想來管理內存,那麼內存回收時就必須能識別哪些對象應放在新生代,哪些對象應放在老年代中。爲了做到這一點,虛擬機給每個對象一個分代年齡(Age)計數器。

如果對象在 Eden 出生並經過第一次 Minor GC 後仍然能夠存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將對象年齡設爲1。對象在 Survivor 中每熬過一次 MinorGC,年齡就增加1歲,當它的年齡增加到一定程度(默認爲15歲,CMS收集器默認6歲,不同的垃圾收集器會略微有點不同),就會被晉升到老年代中。對象晉升到老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold 來設置。

3.5 對象動態年齡判斷

當前放對象的Survivor區域裏(其中一塊區域,放對象的那塊s區),一批對象的總大小大於這塊Survivor區域內存大小的50%(-XX:TargetSurvivorRatio可以指定),那麼此時大於等於這批對象年齡最大值的對象,就可以直接進入老年代了,例如Survivor區域裏現在有一批對象,年齡1+年齡2+年齡n的多個年齡對象總和超過了Survivor區域的50%,此時就會把年齡n(含)以上的對象都放入老年代。這個規則其實是希望那些可能是長期存活的對象,儘早進入老年代。對象動態年齡判斷機制一般是在minor gc之後觸發的。

3.6 老年代空間分配擔保機制

空間擔保指的是老年代爲新生代對象晉升做內存擔保。

在發生Minor GC之前,虛擬機會檢查老年代最大可用的連續空間是否大於新生代所有對象的總空間或者歷次晉升到老年代的對象的平均大小,如果大於,則此次Minor GC是安全的,進行MinorGC,否則進行FullGC.

JDK 6 Update 24之後,-XX:-HandlePromotionFailure不再影響JVM的空間分配擔保策略。

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