1. 垃圾判别方法

引用计数算法

  • 判断对象的引用数量来决定对象是否可以被回收

  • 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1

  • 优点:执行效率高,程序执行受影响小

  • 缺点:无法检测出循环引用的情况,导致内存泄露

可达性分析算法

  • Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活对象

  • 扫描堆中的对象,看是否能沿着GC Root对象为起点的引用链找到该对象,找不到则可以回收

  • 哪些对象可以作为GC Root

  • 通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root

    • 虚拟机栈中的引用的对象

    • 本地方法栈中JNI(natice方法)的引用的对象

    • 方法区中的常量引用的对象

    • 方法区中的类静态属性引用的对象

    • 处于激活状态的线程

    • 正在被用于同步的各种锁对象

    • GC保留的对象,比如系统类加载器等。

2. 垃圾回收算法

标记清除法

  • 标记没有被GC Root引用的对象
  • 清除被标记位置的内存
  • 优点:处理速度快
  • 缺点:造成空间不连续,产生内存碎片
    在这里插入图片描述

标记整理法

  • 标记没有被GC Root引用的对象
  • 整理被引用的对象
  • 优点:空间连续,没有内存碎片
  • 缺点:整理导致效率较低

在这里插入图片描述

复制算法

  • 分配同等大小的内存空间
  • 标记被GC Root引用的对象
  • 将引用的对象连续的复制到新的内存空间
  • 清除原来的内存空间
  • 交换FROM空间和TO空间
  • 优点:空间连续,没有内存碎片
  • 缺点:占用双倍的内存空间
    在这里插入图片描述
                                    ![在这里插入图片描述](https://uploadfiles.nowcoder.com/files/20200522/247775678_1590133187448_20200522081246348.png)
                                    ![在这里插入图片描述](https://uploadfiles.nowcoder.com/files/20200522/247775678_1590133187314_20200522081254168.png)

    3. 分代垃圾回收机制

  • 分代垃圾回收流程
    在这里插入图片描述

    • 对象首先分配在伊甸园区域
    • 新生代空间不足时,触发Minor GC,伊甸园和from存活的对象使用【复制算法】复制到to中,存活的对象年龄加一,并且交换from区和to区
    • Minor GC会引发Stop the world(STW)现象,暂停其他用户的线程。垃圾回收结束后,用户线程才恢复运行
    • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4位二进制)
    • 当老年代空间不足,会先尝试触发Minor GC,如果之后空间仍不足,会触发Full GC(STW时间更长,老年代可能使用标签清除或标记整理算法)
    • 当存放大对象新生代放不下而老年代可以放下,大文件会直接晋升到老年代
    • 当存放大对象新生代和老年代都放不下时,抛出OOM异常
  • 默认堆内存分配
    在这里插入图片描述

    • 新生代占1/3,老年代占2/3
    • -XX:NewRatio:老年代和年轻代内存大小的比例
    • 新生代中按8 1 1进行分配,两个幸存区大小需要保持一致
    • -XX:SurvivorRatio: Eden和Survivor的比值,默认是8(8:1)
  • GC相关VM参数
    在这里插入图片描述

4. 垃圾回收器

  • 安全点(SafePoint)

    • 分析过程中对象引用关系不会发生改变的点

    • 产生安全点的地方:

      • 方法调用
      • 循环跳转
      • 异常跳转
    • 安全点的数量应该设置适中

  • 串行(SerialGC)

    • 单线程的垃圾回收器
    • 堆内存较小,CPU核数少,适合个人电脑
    • SerialGC收集器 (-XX:+UseSerialGC 复制算法) Client模式下默认的年轻代收集器
    • SerialGC Old收集器 (-XX:+UseSerialOldGC 标记-整理算法)Client模式下默认的老年代收集器
      在这里插入图片描述
  • 吞吐量优先(ParallelGC)

    • 多线程的垃圾回收器

    • 堆内存较大,多核CPU,适合服务器

    • 尽可能让单位时间内STW暂停时间最短(吞吐量=运行代码时间/(运行代码时间+垃圾回收时间))

    • 并行的执行

    • ParallelGC收集器(-XX:+UseParallelGC 复制算法) Server模式下默认的年轻代垃圾回收器

    • ParallelGC Old收集器(-XX:+UseParallelOldGC 复制算法)

      在这里插入图片描述

  • 响应时间优先(CMS -XX:+UseConcMarkSweepGC 标记清除算法)

    • 多线程的垃圾回收器

    • 堆内存较大,多核CPU,Server模式下默认的老年代垃圾回收器

    • 尽可能让单次STW暂停时间最短

    • 部分时期内可以并发执行

    • 执行流程

      • 初始标记:stop-the-world
      • 并发标记:并发追溯标记,程序不会停顿
      • 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
      • 重新标记:暂停虚拟机,扫描CMS堆中的剩余对象
      • 并发清理:清理垃圾对象,程序不会停顿
      • 并发重置:重置CMS收集器的数据结构

在这里插入图片描述

  • G1(-XX:+UseG1GC 复制+标记清除算法)

    • G1l垃圾回收器简介
    • 定义:Garbage First (2017 jdk9 默认)
    • 特点
      • 并发和并行
      • 分代收集
      • 空间整合
      • 可预测的停顿
    • 使用场景
      • 同时注重吞吐量和低延迟,默认暂停目标是200ms
      • 超大堆内存,会将整个堆划分为多个大小相等的Region(新生代和老年代不再物理隔离了)
      • 整体上是标记整理算法,两个区域之间是复制算法
  • 垃圾回收阶段

    • 新生代垃圾收集

      • 会发生STW
    • 新生代垃圾收集+并发标记

      • 在Young GC时会进行GC Root的初始标记
      • 老年代占用堆内存空间比例达到阈值时,进行并发标记(不会STW)
    • 混合收集,对新生代,幸存区和老年代都进行收集

      • 最终标记,会STW
      • 拷贝存活,会STW
      • 三种阶段循环交替
        在这里插入图片描述
  • Full GC

    • SerialGC

      • 新生代内存不足发生的垃圾收集:minor GC
      • 老年代内存不足发生的垃圾收集:full GC
    • ParallelGC

      • 新生代内存不足发生的垃圾收集:minor GC
      • 老年代内存不足发生的垃圾收集:full GC
    • CMS

      • 新生代内存不足发生的垃圾收集:minor GC

      • 老年代内存不足

        • 并发收集成功:并发的垃圾收集
        • 并发收集失败:串行的full GC
    • G1

      • 新生代内存不足发生的垃圾收集:minor GC

      • 老年代内存不足,达到阈值时进入并发标记和混合收集阶段

        • 如果回收速度>新产生垃圾的速度 :并发垃圾收集
        • 如果回收速度<新产生垃圾的速度:串行的full GC

          5. 四种引用

          在这里插入图片描述
          • 强引用
      • 最常见的对象:通过new关键字创建,通过GC Root能找到的对象。

      • 当所有的GC Root都不通过【强引用】引用该对象时,对象才能被垃圾回收

      • 软引用

      • 仅有【软引用】引用该对象时,在垃圾回收后,内存仍不足时会再次发起垃圾回收,回收软引用对象

      • 可以配合引用队列来释放软引用自身

      • 创建一个软引用:SoftReference<object> ref = new SoftReference<>(new Object());</object>

      • 软引用被回收后,仍然还保留一个null,如将软引用加入集合,回收后遍历集合仍然还存在一个null

        • 解决:使用引用队列,软引用关联的对象被回收时,软引用自身会被加入到引用队列中,通过queue.poll()取得对象进行删除
        • 创建一个而引用队列:ReferenceQueue<object> queue = new ReferenceQueue<>();</object>
        • 创建加入了引用队列的软引用:SoftReference<object> ref = new SoftReference<>(new Object(),queue);</object>
  • 弱引用

    • 仅有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象

    • 可以配合引用队列来释放弱引用自身

    • 创建一个弱引用:WeakReference<object> ref = new WeakReference<>(new Object());</object>

    • 引用队列使用同软引用

      • 虚引用
    • 必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将【虚引用】入队,由Reference Hanler线程调用虚引用相关方法释放【直接内存】(unsafe类中方法)

      • 终结器引用
    • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用队列入队(引用对象暂未回收),再由Finalizer线程通过终结器引用找到被引用对象并调用他的finalize方法,第二次gc时回收被引用对象