我们来聊聊JVM垃圾回收

我们的对象被分配在哪里

首先来看这么一段代码:

public class Test {
    private static Teacher teacher = new Teacher();

    public static void main(String[] args) {
       load();
    }

    public static void load() {
        User user = new User();
    }
}

上面的对象分配如下
在这里插入图片描述
首先是静态类变量分配在方法区中,然后在堆新生代创建Teacher,由teacher指向
其次是在main方法中执行 load方法,在java虚拟机栈压栈,创建一个load栈帧,栈帧中包含user局部变量,同时在堆内存中创建User实例对象,由user指向User实例对象
需要注意的是基本所有的对象创建都是在新生代,除非是大对象直接进入老年代

GC分类

gc一般分为miner gc 和full gc

  • miner gc:发生在新生代的垃圾回收,速度较快
  • full gc: 发生在老年代的垃圾回收,速度很慢

什么时候发生GC

  • 什么时候触发miner gc?
    可以看到在load方法执行后,出栈,user变量销毁,User对象就没有有变量指向了,成了需要回收的垃圾对象。当新生代里的对象越来越多,都快满了,此时就会触发垃圾回收,把新生代没有人引用的对象给回收掉,释放内存空间。这个过程叫miner gc。
  • 什么时候触发full gc呢?
    有如下几个条件
  1. 老年代可用内存小于新生代全部对象大小,没有开启空间担保参数
  2. 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小
  3. 新生代Minor GC后存活对象大于Survivor,进入老年代,此时老年代内存不足
  4. 老年代占比默认超过92%

聊聊GC收集算法

我们GC垃圾回收会如何回收呢?一般有如下几种算法

  1. 复制算法
    简单来说将新生代内存分为两块,S1快用于存放现在存活的对象,发生垃圾回收后就把S1存活的对象复制到S2,清空S1,后续新建的对象分配到S2,下次垃圾回收清空S2如此循环
    在这里插入图片描述
    这种算法的缺点很明显,假如我们给新生代1G的内存空间,那么只有512MB的内存空间是可以用的
  2. 标记清除算法
    这种算法就是我们在新生代中将存活的对象标记出来,然后直接清除,这样看好像是用到了所有内存
    在这里插入图片描述
    这样我们只需要将user对象清除就好了,但是会带来另一个问题:内存碎片,由于这些存活的对象分布不均,产生了许多内存碎片也会造成大量内存浪费

复制算法的优化

复制算法的缺点很明显,我们如何优化呢,将年轻代分为 :Eden区和Survivor区
在这里插入图片描述
每次分配对象在Eden区分配,当Eden区满了后就将Eden存活的对象和Survivor存活的对象移到另一个Survivor,这样可以保证使用内存为Eden区和一个Survivor,减少了内存的浪费,同时每次都是清除Eden和一个Survivor,不会产生内存碎片

垃圾收集器

目前主要的垃圾收集器有如下几种

  1. Serial收集器
    使用新生代垃圾回收期,单线程收集,适合用于单核CPU操作系统使用。目前都是多核系统,所以Serial收集器基本已经被淘汰了
    在这里插入图片描述
  2. Serial Old收集器
    老年代垃圾回收器,也是单线程收集,同Serial收集器一样,不同的是Serial Old还会用到,就是当老年代发生Concurrent Mode Failure,系统就会由CMS垃圾收集器强制切换使用Serial Old收集器
    在这里插入图片描述
  3. ParNew收集器
    目前用的最多的新生代垃圾收集器,多线程收集,收集线程默认和CPU核数保持一致,如果要设置可通过参数 -XX:ParallelGCThreads设置,采用的算法是优化的复制算法
    在这里插入图片描述
  4. CMS收集器
    目前使用最多的老年代垃圾收集器,CMS垃圾收集器过程比较复杂包括这五个过程:
    1. 初始标记:这个阶段会Stop the World,作用就是标记处所有的GC Roots直接引用的对象,但是耗时不会太多,因为仅仅标记GC Roots直接引用的对象

    2. 并发标记:这个阶段不会发生Stop the World,作用是已存在对象进行GC Root追踪,耗时最久,因为需要追踪所有对象是否从根源上被GC Roots引用,但是不用担心,因为这时候系统是可以运行的

    3. 重新标记:这个阶段会Stop the World,作用就是标记因为并发标记系统运行新增的存活对象和垃圾对象,耗时不多,因为2阶段已经标记大量对象,这里只是标记新增的少量对象

    4. 并发清除:这个阶段不会Stop the World,作用就是清理标记出来的垃圾对象,耗时会很久,但是系统可以运行,所以不用担心

    5. 碎片整理:这个阶段也会 Stop the World。默认每次full gc后都会对内存进行整理,因为使用的标记清除算法,会产生内存碎片,所以需要碎片整理。是否需要每次full gc后整理还是full gc几次后整理可由参数 --XX:+UseCMSCompactAtFullCollection 设置
       CMS对full gc作了大量优化,就是为了减少full gc时间,但是full gc时间相对minor gc来说时间还是很久,所以一般jvm调优就是尽量减少full gc。其次CMS垃圾收集器也是有一些缺点的:
      1.耗用CPU,CMS默认垃圾回收线程数量:(CPU核数 + 3) / 4

    6. 浮动垃圾:并发清除阶段系统继续运行,然后一些对象进入老年代,同时又变成垃圾对象,CMS只会回收之前标记出来的垃圾对象,这些浮动垃圾需要下次full gc才能回收
      在这里插入图片描述

  5. G1垃圾收集器
    最新的G1垃圾收集器,统一收集新生代和老年代,和传统的几种垃圾收集器有所不同又有所相同,G1垃圾收集器是把堆内存分为很多个Region,其中Region即有新生代,也有老年代,而且Region也不是固定的
    在这里插入图片描述

G1和CMS后续会对这些垃圾收集器作详细说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值