实战模拟对象进入老年代的三种方式

我知道大家经常会被问这样的面试题-对象在什么情况情况下会进入老年代,答案我想大家都是知道的,但是更详细一点的,到底是年龄到了15就进入老年代呢,还是15的下一次young GC进入老年代,最大年龄设置为0会是什么情况,设置为16又会是什么情况?让我们用代码来实际操作看下。

1)动态年龄判断,当幸存区中年龄1+年龄2+年龄3+...+年龄n-1<50%,年龄1+年龄2+年龄3+...+年龄n-1+年龄n>50%,那么幸存区中年龄n及其以上的对象就会放到老年代中。

2)年龄到达阈值,通过-XX:MaxTenuringThreshold进行设置,默认为15

3)大对象,因为程序一般认为大对象有可能是需要存活比较长时间的对象,而且大对象在幸存区返回来回复制,影响young GC的效率,通过-XX:PretenureSizeThreshold进行设置。


实战演练

首先是JVM配置

-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=5242880 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

相关配置释意:新生代 10M 、伊甸区 8M、幸存区1 1M、幸存区2 1M、堆 20M、老年代 10M、年龄阈值是15、大对象是 10M;使用ParNewGC+CMS;打印相关gc日志到gc.log文件

1)动态年龄判断

	public static void main(String[] args) {
        byte[] array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//2M
        array1 = null;
        array1 = new byte[2*1024*1024]; //第一次younggc 把初始对象放置到幸存区 ,大小为562K,比例超过50%,年龄为1 
    }

 执行以上代码,在执行到最后一行的时候,伊甸区内存不够,导致了首次young GC,562K的初始化对象放置于幸存区,年龄为1

    public static void main(String[] args) {
        byte[] array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//2M
        array1 = null;
        array1 = new byte[2*1024*1024]; //第一次younggc 把初始对象放置到幸存区 ,比例超过50%,年龄为1
        array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//2M
        array1 = new byte[2*1024*1024];//第二次younggc 把初始对象全部移到老年代
    }

  执行以上代码,在执行到最后一行的时候,伊甸区内存不够,导致了第二次young GC,562K的初始化对象放置于幸存区(初始化对象可以会有部分变化),年龄为1,因为年龄为1的对象>50%,所以562K的对象全都放到老年代。


2)年龄到达阈值

    public static void main(String[] args) {
        // 1024*1024  是 1M
        byte[] array1 = new byte[2*1024*1024];
        array1 = new byte[2*1024*1024];
        array1 = new byte[2*1024*1024];
        array1 = new byte[128*1024];
        array1 = null;
        
        array1 = new byte[2*1024*1024]; //第一次younggc 把初始对象放置到幸存区 ,比例超过50%,年龄为1
        array1 = new byte[2*1024*1024];
        array1 = new byte[2*1024*1024];
        array1 = null;
        
        byte[] array2 = new byte[128*1024];
        
        array1 = new byte[2*1024*1024]; //第二次younggc 把初始对象放置到老年代 ,array2放置到幸存区,年龄为1
        array1 = new byte[2*1024*1024];
        array1 = new byte[2*1024*1024];
        array1 = new byte[128*1024];
        array1 = null;
        
        array1 = new byte[2*1024*1024];//第三次younggc,array2仍然在幸存区,年龄为2
        array1 = new byte[2*1024*1024];
        array1 = new byte[2*1024*1024];
        array1 = new byte[128*1024];
        array1 = null;
        
        int i = 0;
        while(i<15) {
        	array1 = new byte[2*1024*1024];//第三到十八次younggc,array2仍然在幸存区,年龄为3-15
            array1 = new byte[2*1024*1024];
            array1 = new byte[2*1024*1024];
            array1 = new byte[128*1024];
            array1 = null;
            i++;
        }
    }

以上gc过程中, array2 对象是在首次young GC之后一直存活的,在第二次young GC进入老年代,年龄为1,第16次(array2 经历的第15次)young GC时,array2仍然在老年代中,此时年龄为15,第17次(array2 经历的第16次)young GC,array2进入老年代。

如果-XX:MaxTenuringThreshold=16,启动会是怎么样的?

因为对象头用于表示年龄的长度就是4个bit,范围就会0~15,所以16是无效设置,所以那些说阈值设置大一点,就是在幸存区待的久一点的,是不对的,自己动动手,不信谣,不传谣。

如果-XX:MaxTenuringThreshold=0,启动会是怎么样的?

所有对象在young GC时,跳过幸存区,直接进入老年代。

3)大对象

	public static void main(String[] args) {
        byte[] array1 = new byte[6*1024*1024];//6M
    }

由gc日志可知,没有 young GC一次,老年代已经使用了6M左右,这个就是大对象直接进入老年代

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值