Java对象晋升的四种方式


四种方式

  • 担保机制
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
  • 动态对象年龄判断

担保机制

当新生代中无法容纳新对象时,则JVM会通过担保机制将大对象直接放到老年代中。如下图:

image-20220720141422718

代码:

public class test1 {

    /*JVM参数:
        -XX:+PrintGCDetails -verbose:gc -Xms30M -Xmx30M

        参数说明
         * -XX:+PrintGCDetails  程序执行完成后打印内存使用情况
         * -verbose:gc   每次GC后打印日志
         * -Xms30M 初始堆内存
         * -Xmx30M 最大堆内存
     */
    public static int _1M = 1024 * 1024;

    public static void main(String[] args) {
        byte[] bytes1 = new byte[2 * _1M];
        byte[] bytes2 = new byte[2 * _1M];
        byte[] bytes3 = new byte[2 * _1M];
        byte[] bytes4 = new byte[6 * _1M];
    }
}

运行结果:6M的对象直接放入老年代

image-20220720140504580

大对象直接进入老年代

大对象的阈值:通过用户指定:-XX:PretenureSizeThreshold=对象大小(单位:byte)

需要注意的是:PretenureSizeThreshold参数只对Serial和ParNew两款收集器有效

代码:

public class test1 {

    /*
    JVM参数:
    -XX:+PrintGCDetails -verbose:gc -Xms30M -Xmx30M  -XX:+UseSerialGC -XX:PretenureSizeThreshold=3145728

    参数说明
     * -XX:+PrintGCDetails  程序执行完成后打印内存使用情况
     * -verbose:gc   每次GC后打印日志
     * -Xms30M 初始堆内存
     * -Xmx30M 最大堆内存
     * -XX:+UseSerialGC 使用 SerialGC 回收器
     * -XX:PretenureSizeThreshold=3145728 设置对象超过3M时直接晋升到老年代
     */
    public static int _1M = 1024 * 1024;

    public static void main(String[] args) {
        byte[] bytes1 = new byte[4 * _1M];
    }
}

运行结果:超过设定大小3M的对象直接进入老年代

image-20220720142237948

长期存活的对象进入老年代

对象在Eden区出生,存活年龄默认为1,在每次新生代回收(Minor GC)后,存活的对象年龄加1,当新生代中对象的年龄到达一个阈值后(jdk1.8中默认是15,最大也是15),晋升至老年代。此外,年龄阈值可以通过JVM参数:-XX:MaxTenuringThreshold手动设置,范围在0~15之间。

动态对象年龄判断

虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果在Survivor区中相同年龄(设年龄为age)的对象的所有大小之和超过Survivor空间的一半,年龄大于或等于该年龄(age)的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。


文章作者: 一袖南烟顾
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 一袖南烟顾 !
评论
  目录