分代回收算法之新生代垃圾回收流程

新生代垃圾回收流程

JVM将堆内存分为新生代老年代,它们因为各自的特点而采用不同的垃圾回收算法来进行垃圾回收。

具体的可以参考另一篇文章:JVM四种垃圾回收算法https://blog.csdn.net/sunao1106/article/details/126690119?spm=1001.2014.3001.5501

这里主要给大家讲一下新生代中是如何进行垃圾回收的

新生代中使用四种垃圾回收算法中的复制算法来进行垃圾回收的,它将新生代又分为了Edensurvivor from以及survivor to三个区域,并且它们的内存比例为8:1:1,比如一个10MB的新生代,会给Eden区分配8MB,两个survivor各分配1MB,但其实供我们使用的内存大小只有9MB,因为其中一个survivor区(survivor to)只是用来协助我们做复制操作的,它不被用来存放对象,看完下面的例子,你会明白。
在这里插入图片描述
新生代垃圾回收流程:

  • 在Eden区内存够的情况下,创建的对象会优先选择放到Eden区;
  • 如果在存入对象时,Eden区的内存不够存的,那就会进行一次minor gc垃圾回收,也就是将Eden区中存活的对象复制到survivor from中,然后再清空Eden区;
  • 之后再进来的对象还是会选择放到Eden区,如果Eden区又存放不下了,这时就会将Eden区和survivor from中存活的对象都复制到survivor to中,然后清空Eden区和survivor from,并且将survivor from和survivor to进行位置交换,目的就是为了保证survivor to中不存放对象。如果这个时候新生代还是放不下这个对象,那该对象就会被放到老年代中。
  • 注意,再上述的操作中,对象每移动一次位置,都会给其年龄+1(一个标识),当一个对象的年龄达到15时,就会将该对象纳入老年代中。

    也很好理解,新生代的回收频率比较高,一般就是用来存放一些生存率较低的对象,而老年代中就是存放那些经常使用、不容易被垃圾回收的对象。

案例演示

首先为了方便效果,在idea中配置了一些虚拟机参数:-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc,大概解释一下:

  • -Xms20M、 -Xmx20M:设置堆的初始内存大小为20M,最大内存大小为20M;
  • -Xmn10M:设置新生代的内存大小为10M;
  • XX:+UseSerialGC:使用串行垃圾回收器;
  • -XX:+PrintGCDetails -verbose:gc:打印GC详细信息。

看下面一段代码:

public class GCDemo01 {
    private static final int _512KB = 1024 * 512;
    private static final int _7MB = 1024 * 1024 * 7;
    private static final int _8MB = 1024 * 1024 * 8;
    private static final int _1MB = 1024 * 1024;
    private static final int _2MB = 1024 * 1024 * 2;
    
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();
        list.add(new byte[_8MB]);
    }
}

执行一下:
在这里插入图片描述

可以看到Eden区被分配了8192K,两个survivor区各自1024K,正好10M左右。老年代为10240K,合起来刚好是我们分配的20M对空间大小。

也可以看到其内存占用情况,Eden区一开始就被占用了28%,也就是说Eden区还有差不多不到6M左右的剩余内存大小。老年代被占用了0%。

下面继续执行一段代码:
在这里插入图片描述
向list中添加5M大小的对象,此时可以看到,Eden区已经被占用了90%了。
在这里插入图片描述

如果不出意外,我们再去添加1MB的对象,Eden区就放不下了,继续看看他做了什么操作。
在这里插入图片描述
我们可以清除了看到,它进行了一次GC操作(minor gc),也就是将Eden区冲存活的对象复制到survivor from中,再对Eden区进行清空,此时Eden占用了13%,survivor from占用了64%。
在这里插入图片描述

接下来,如果我们再次存入对象,Eden区又存不下了,那么就会将Eden区和survivor from中存活的对象都复制到survivor to中,然后再清空Eden区和survivor from。

这里代码不好演示,因为我们代码中所有的对象都是强引用,就如上图所示,存放在survivor from中的对象都是强引用,也就是存活对象,我画个图来说明这个过程。

比如,在上一次GC之后,内存结构像这样:
在这里插入图片描述
这个时候,我们又存入了一个10MB的对象,发现Eden存不下了,于是又进行一次GC,也就是将Eden区和survivor from中存活的对象都复制到survivor to中,然后再清空Eden区和survivor from。
在这里插入图片描述
注意,这个时候并没有结束,随后又将survivor from和survivor to进行一次交换。
在这里插入图片描述
也就是确保survivor to不存放对象,就如上述所说的,这个区域就是用来协助我们进行对象复制的。

之后的每一次Eden区内存不够放,都按上述的操作来进行垃圾回收,如果在垃圾回收之后,发现还是不够放,那就将整个新生代的对象都放到老年代中,再清空它。

就比如,在我们上述例子中,存入12MB大小的对象。
在这里插入图片描述
可以看到,此时老年代占了86%,新生代只有eden区占用了53%。

若是老年代也满了,那就进行一次full gc,也就是新生代、老年代都进行回收。

若此时,还是存放不下,那就只好抛出OutOfMemoryError

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值