JVM內存分配與回收策略分析

本文的測試結果都是基於jdk1.8以及Serial + Serial Old的收集器組合。

新建的對象優先在Eden分配

一般來說,我們代碼中創建的對象會優先在Eden區域分配,如下圖:
在這裏插入圖片描述
我們通過代碼來感受一下:

public class Main {

    /**
     * -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC
     */
    public static void main(String[] args) {

    }
}

GC日誌輸出:

Heap
 def new generation   total 9216K, used 2155K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  26% used [0x00000000fec00000, 0x00000000fedc5c18, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 2930K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 317K, capacity 388K, committed 512K, reserved 1048576K
public class Main {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[2 * MB_SIZE];
    }
}

GC日誌輸出:

Heap
 def new generation   total 9216K, used 4203K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff01ade0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3443K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K

對比以上兩份代碼,可以看到第一份代碼中我們沒有手動創建代碼,新生代使用了2155K內存,eden區域已使用內存佔比26%,而第二份代碼中,我們手動創建了一個2M大小的對象,新生代使用了4203K內存,eden區域已使用內存佔比51%,正好比上份代碼多使用了2048K內存,可知這2M的對象分配在了eden區域。

那隨着對象的不斷創建,eden區域遲早會變滿,剩餘的內存已不足以存放新建的對象,此時虛擬機就會發起一次Minor GC。將eden和Survivor中還存活的對象轉移到另一個Survivor區域中,然後將剩餘的空間一次性清空,供新建的對象存放。注意:新建的對象只會在Eden區域分配的,而Survivor區域是存放每次GC過後存活的對象,GC過後永遠會有一塊Survivor是空的。
在這裏插入圖片描述
通過代碼來看一下:

public class Main {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[2 * MB_SIZE];
        byte[] a2 = new byte[2 * MB_SIZE];
        byte[] a3 = new byte[2 * MB_SIZE];
        a1 = null;
        a2 = null;
        a3 = null;
        byte[] a4 = new byte[5 * MB_SIZE];
    }
}

GC日誌輸出:

[GC (Allocation Failure) [DefNew: 8135K->618K(9216K), 0.0009852 secs] 8135K->618K(19456K), 0.0010119 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 5904K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  64% used [0x00000000fec00000, 0x00000000ff129790, 0x00000000ff400000)
  from space 1024K,  60% used [0x00000000ff500000, 0x00000000ff59a948, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3443K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K

可以看到,當我創建a4時,由於內存不足,導致發生了GC,發生的區域在新生代,614K左右的存活內存被移動到另一個Survivor區域中,之後在eden區域存放這5M的對象。其實,這裏還有一個問題,如果GC過後存活的對象在Survivor中放不下怎麼辦?這時,就需要用到了老年代空間分配擔保規則,將這些對象直接放入到老年代中。

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

在程序運行的過程中,總有一些對象存活時間較長,躲過一次又一次的GC,但是它們也要不停的在兩個Survivor區域中轉移,這樣就比較浪費時間,因爲每次GC呢它們不會被清理掉,又要每次不停的轉移,所以說,如果一個對象活得夠長了,乾脆直接將它們轉移到老年代中,不讓它們再參與新生代的GC。

public class Demo {
    private static Object object = new Object(); 
}

就如上述代碼,只要Demo類還在,沒有卸載,則object這個靜態變量一直存在,對Object這個對象一直引用着,無論新生代怎麼垃圾回收,這個對象都不會回收。如果對象經過新生代的垃圾回收後仍然存活並且被轉移到一塊Survivor區域,則對象的年齡增加1。每經過一次這樣的過程,對象年齡就增加1,當年齡到了一定程度了,就將它轉移到老年代中。可以通過設置參數-XX:MaxTenuringThreshold來設置具體是多少歲進入老年代,默認15。經過第一次gc後存活的年齡爲1
在這裏插入圖片描述
測試代碼如下:

public class Demo2 {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms200M -Xmx200M -Xmn100M -XX:+PrintGC -XX:+PrintGCDetails -XX:MaxTenuringThreshold=3 -XX:+PrintTenuringDistribution -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[MB_SIZE];
        byte[] a2 = new byte[MB_SIZE * 70];
        // gc3次
        for (int i = 0; i < 3; i++) {
            a2 = null;
            a2 = new byte[70 * MB_SIZE];
        }
    }
}

GC日誌輸出:

[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   1:    1684360 bytes,    1684360 total
: 77619K->1644K(92160K), 0.0031652 secs] 77619K->1644K(194560K), 0.0032269 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   2:    1680680 bytes,    1680680 total
: 73324K->1641K(92160K), 0.0024464 secs] 73324K->1641K(194560K), 0.0024812 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   3:    1680464 bytes,    1680464 total
: 74959K->1641K(92160K), 0.0012489 secs] 74959K->1641K(194560K), 0.0012832 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 92160K, used 74140K [0x00000000f3800000, 0x00000000f9c00000, 0x00000000f9c00000)
  eden space 81920K,  88% used [0x00000000f3800000, 0x00000000f7ecce50, 0x00000000f8800000)
  from space 10240K,  16% used [0x00000000f9200000, 0x00000000f939a450, 0x00000000f9c00000)
  to   space 10240K,   0% used [0x00000000f8800000, 0x00000000f8800000, 0x00000000f9200000)
 tenured generation   total 102400K, used 0K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
   the space 102400K,   0% used [0x00000000f9c00000, 0x00000000f9c00000, 0x00000000f9c00200, 0x0000000100000000)
 Metaspace       used 3213K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

把內存設置大一點主要是避免動態對象年齡判定。爲MaxTenuringThreshold設置爲3,當a1經過3次gc時,可以看到,a1還留在Survivor區域中,老年代中還沒有對象。

public class Demo2 {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms200M -Xmx200M -Xmn100M -XX:+PrintGC -XX:+PrintGCDetails -XX:MaxTenuringThreshold=3 -XX:+PrintTenuringDistribution -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[MB_SIZE];
        byte[] a2 = new byte[MB_SIZE * 70];
        // gc4次
        for (int i = 0; i < 4; i++) {
            a2 = null;
            a2 = new byte[70 * MB_SIZE];
        }
    }
}

GC日誌輸出:

[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   1:    1684360 bytes,    1684360 total
: 77619K->1644K(92160K), 0.0034374 secs] 77619K->1644K(194560K), 0.0035061 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   2:    1680680 bytes,    1680680 total
: 73324K->1641K(92160K), 0.0022057 secs] 73324K->1641K(194560K), 0.0022525 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
- age   3:    1680464 bytes,    1680464 total
: 74959K->1641K(92160K), 0.0018657 secs] 74959K->1641K(194560K), 0.0019163 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 3 (max 3)
: 73321K->0K(92160K), 0.0023300 secs] 73321K->1641K(194560K), 0.0023680 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 92160K, used 72499K [0x00000000f3800000, 0x00000000f9c00000, 0x00000000f9c00000)
  eden space 81920K,  88% used [0x00000000f3800000, 0x00000000f7ecce50, 0x00000000f8800000)
  from space 10240K,   0% used [0x00000000f8800000, 0x00000000f8800000, 0x00000000f9200000)
  to   space 10240K,   0% used [0x00000000f9200000, 0x00000000f9200000, 0x00000000f9c00000)
 tenured generation   total 102400K, used 1641K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
   the space 102400K,   1% used [0x00000000f9c00000, 0x00000000f9d9a450, 0x00000000f9d9a600, 0x0000000100000000)
 Metaspace       used 3213K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

從上面的日誌可以看到第四次gc後新生代沒有對象了,從73321K變爲0K,全轉移到老年代了,說明a1經過了第四次gc後被轉移到老年代了。默認設置下,MaxTenuringThreshold=15,即在第16次gc後轉移到老年代。

動態對象年齡判定規則

還要一個跟對象年齡有關的就是這個動態對象年齡判定規則。虛擬機中對象年齡不一定非要等到MaxTenuringThreshold所設置的值纔要轉移到老年代,《深入理解Java虛擬機》裏對這個規則的描述是:“如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡”。一開始我對這句話的理解是一定要相同年齡的對象大小超過50%的,但如果是這樣的話,比如,年齡爲3的對象大小超過了Survivor區域的一半了,年齡都爲3,說明它們都是經過3次gc存活下來的,都是從年齡爲2的時候經過gc存活下來的,那在年齡爲2的時候這些對象大小就應該超過了Survivor的一半了,同理,年齡爲1的時候也一樣,也就是說如果有同齡的對象大小超過了Survivor的一半,就只能是年齡爲1的對象,而根據動態對象年齡判定規則,年齡大於等於1的就得轉移到老年代中,那根據以上的推論,根本就不會出現年齡大於1的同齡對象總大小超過Survivor區域的一半。如果有,早在年齡爲1的時候這些對象就已經轉移到老年代了。所以,我覺得這句話沒表述清楚,容易發生歧義。

正確的理解是這樣的:Survivor中的對象年齡從小到大進行累加,當加入某個年齡段後,累加和超過survivor區域50%的時候,就將這個年齡以及往上的年齡都轉移到老年代,無須等到MaxTenuringThreshold中要求的年齡。比如Survivor空間中年齡爲1的對象佔用20%,年齡爲2的對象佔用40%, 年齡爲3的對象佔用30%。從低年齡開始累加,年齡爲1加上年齡爲2的對象大小佔用60%,超過了默認的50%,則就將年齡爲2以及往上的對象轉移到老年代。
在這裏插入圖片描述
測試代碼
gc兩次:

public class Demo2 {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms200M -Xmx200M -Xmn100M -XX:+PrintGC -XX:+PrintGCDetails -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[MB_SIZE * 3];
        byte[] a2 = null;
        byte[] a3 = new byte[MB_SIZE * 70];
        // gc兩次
        for (int i = 0; i < 2; i++) {
            a3 = null;
            a3 = new byte[70 * MB_SIZE];
            if (i == 0) {
                a2 = new byte[MB_SIZE * 3];
            }
        }
    }
}

GC日誌:

[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 15 (max 15)
- age   1:    3781512 bytes,    3781512 total
: 79667K->3692K(92160K), 0.0041238 secs] 79667K->3692K(194560K), 0.0041744 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 2 (max 15)
- age   1:    3145744 bytes,    3145744 total
- age   2:    3777832 bytes,    6923576 total
: 80032K->6761K(92160K), 0.0055647 secs] 80032K->6761K(194560K), 0.0056018 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 92160K, used 80899K [0x00000000f3800000, 0x00000000f9c00000, 0x00000000f9c00000)
  eden space 81920K,  90% used [0x00000000f3800000, 0x00000000f8066800, 0x00000000f8800000)
  from space 10240K,  66% used [0x00000000f8800000, 0x00000000f8e9a538, 0x00000000f9200000)
  to   space 10240K,   0% used [0x00000000f9200000, 0x00000000f9200000, 0x00000000f9c00000)
 tenured generation   total 102400K, used 0K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
   the space 102400K,   0% used [0x00000000f9c00000, 0x00000000f9c00000, 0x00000000f9c00200, 0x0000000100000000)
 Metaspace       used 3214K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

通過日誌可以看出:經過兩次gc後年齡1加上年齡2已經達到6923576 bytes,超過了Survivor的一半5242880 bytes,所以閾值threshold動態調整爲2,下次gc後年齡大於2並存活的對象就會轉移到老年代中。如下測試:
gc三次:

public class Demo2 {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms200M -Xmx200M -Xmn100M -XX:+PrintGC -XX:+PrintGCDetails -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[MB_SIZE * 3];
        byte[] a2 = null;
        byte[] a3 = new byte[MB_SIZE * 70];
        for (int i = 0; i < 3; i++) {
            a3 = null;
            a3 = new byte[70 * MB_SIZE];
            if (i == 0) {
                a2 = new byte[MB_SIZE * 3];
            }
        }
    }
}

GC日誌:

[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 15 (max 15)
- age   1:    3781512 bytes,    3781512 total
: 79667K->3692K(92160K), 0.0044879 secs] 79667K->3692K(194560K), 0.0045403 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 2 (max 15)
- age   1:    3145744 bytes,    3145744 total
- age   2:    3777832 bytes,    6923576 total
: 80032K->6761K(92160K), 0.0068550 secs] 80032K->6761K(194560K), 0.0069083 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 5242880 bytes, new threshold 15 (max 15)
- age   2:    3145744 bytes,    3145744 total
: 80079K->3072K(92160K), 0.0047592 secs] 80079K->6761K(194560K), 0.0047949 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 92160K, used 76390K [0x00000000f3800000, 0x00000000f9c00000, 0x00000000f9c00000)
  eden space 81920K,  89% used [0x00000000f3800000, 0x00000000f7f99b20, 0x00000000f8800000)
  from space 10240K,  30% used [0x00000000f9200000, 0x00000000f9500010, 0x00000000f9c00000)
  to   space 10240K,   0% used [0x00000000f8800000, 0x00000000f8800000, 0x00000000f9200000)
 tenured generation   total 102400K, used 3689K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
   the space 102400K,   3% used [0x00000000f9c00000, 0x00000000f9f9a450, 0x00000000f9f9a600, 0x0000000100000000)
 Metaspace       used 3214K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

可以看到,Survivor中不存在年齡爲3的對象,都轉移到老年代去了,只剩下年齡爲2的對象了,並且閾值重新調整爲15 。

其實,無論是默認超過15歲就進入老年代,還是動態對象年齡判定規則,都是希望那些可能是長期存活的對象,今早進入到老年代,避免在新生代中佔用大量內存,影響效率以及頻繁觸發GC。

大對象直接進入到老年代

如果遇到一個大對象,則直接將它分配到老年代去,避免遇到一個存活時間長,屢次躲過GC的大對象,在兩個Survivor區域間來回移動,浪費時間。可以通過-XX:PretenureSizeThreshold參數來設置大對象的閾值,即多大的對象直接分配到老年代,比如:-XX:PretenureSizeThreshold=3145728,其中3145728指字節數,即超過3M的對象直接分配到老年代。如下圖:
在這裏插入圖片描述
代碼測試如下:

public class Main {

    private static final int MB_SIZE = 1024 * 1024;

    /**
     * -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=3145728 -XX:+UseSerialGC
     */
    public static void main(String[] args) {
        byte[] a1 = new byte[4 * MB_SIZE];
    }
}

GC日誌輸出:

Heap
 def new generation   total 9216K, used 1982K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  24% used [0x00000000fec00000, 0x00000000fedefa40, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 3101K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 336K, capacity 388K, committed 512K, reserved 1048576K

可以看到10M的老年代被使用了40%,即4M的大對象已直接分配到了老年代。注意:PretenureSizeThreshold參數只對Serial和ParNew兩種垃圾收集器有效。

空間分配擔保規則

上面講到了,如果GC過後存活的對象在Survivor中放不下了,就需要用到了老年代空間分配擔保規則,將這些對象直接放入到老年代中,現在就來講講這個空間分配擔保規則是怎樣的。

首先,在執行任何一次Minor GC之前,虛擬機都會先檢查一遍老年代中最大的可用的連續空間是否大於新生代中對象的總大小。

爲什麼要檢查呢?因爲在極端情況下,新生代在GC後對象全部存活,此時存活對象可能比較大,一塊Survivor區域放不下了,就會轉移到老年代中。所以如果老年代中最大的可用的連續空間大於新生代中對象的總大小,那就算新生代的對象都轉移過來,老年代也是有空間可以放得下的,此時就可以放心的Minor GC了。

但如果老年代中最大的可用的連續空間小於新生代中對象的總大小,那就可能出現老年代也沒有足夠的連續空間來放這些對象。所以如果檢查發現老年代中最大的可用的連續空間小於新生代中對象的總大小,就會查看HandlePromotionFailure設置值是否允許擔保失敗。

如果有設置HandlePromotionFailure這個參數,則再檢查老年代最大的可用的連續空間是否大於之前歷次Minor GC後進入老年代對象的平均大小。因爲新生代一共會有多少對象活下來在實際完成內存回收之前是無法明確知道,所以虛擬機只好以之前每一次進入老年代對象的平均大小作爲參考,認爲很可能這次進入老年代對象的大小跟平均大小差不多。所以若大於,則可以認爲這次晉升的對象老年代可以放得下,可嘗試進行一次Minor GC(這次Minor GC是有風險的,因爲會出現這次Minor GC後存活的對象比平均值大並且也比老年代最大的可用的連續的空間大,那也會導致擔保失敗,老年代放不下這些對象。若出現這種情況,虛擬機會發起一次Full GC。若GC過後老年代還是放不下,就會導致OOM內存溢出)。若小於或者HandlePromotionFailure參數沒有設置,則虛擬機就會直接進行一次Full GC,讓老年代騰出一些空間,而後再進行Minor GC。

單看文字可能說不清,畫個簡單的流程圖:
在這裏插入圖片描述
這個圖可能不一定正確,只是一個簡單的流程。

但是不同版本規則有點不同,引用《深入理解Java虛擬機》中的一句話:“JDK6 Update 24之後的規則變爲只要老年代的連續空間大於新生代對象總大小或者歷次晉升的平均大小就會進行Minor GC, 否則將進行Full GC”。之後的版本HandPromotionFailure參數不會再影響到虛擬機的空間分配擔保策略,可以認爲強制打開了HandlePromotionFailure設置。

參考資料

《深入理解Java虛擬機》
jvm誤區–動態對象年齡判定
你不知道的晉升閾值TenuringThreshold詳解

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