JVM 垃圾回收算法

JVM 内存区域:地址
JVM 垃圾收集器:地址

文章目录

    • 内存分配
    • 对象是否需要回收
      • 引用计数法
      • 可达性分析法
      • 引用详解
    • 垃圾回收算法
      • 标记-清除算法
      • 复制算法
      • 标记-压缩算法
      • 分代收集算法
    • 垃圾收集器


内存分配

在内存分配的文章中有提到:

  • 目前主流的垃圾收集器都是采用分代回收算法(新生代和老年代),再根据不同年代的特点选择合适的垃圾收集算法。
  • 对象是优先在 eden 区分配的,当 eden 区没有足够空间时会先进行一次 Minor GC,如果GC后空间依然不够则会直接进入老年代(分配担保机制)。

Minor GC 和 Full GC

  • Minor GC: 新生代GC,发生在新生代的垃圾收集动作,次数频繁效率快。
  • Full GC: 老年代GC,发生在老年代,速度会比 Minor GC 慢。

会进入老年代的对象:

  1. 大对象: 需要大量连续内存空间的对象,避免由于分配担保机制带来的复制而降低效率。
  2. 长期存活的对象: 对象经过一次 Minor GC 还存活则进入 Survivor 区,且对象的年龄计数器会被设置为 1,每 GC 一次就加 1直到到达老年代阈值(默认15)就会进入老年代。

注: 如果如果 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到要求的年龄。


对象是否需要回收

引用计数法

原理:
给每个对象添加一个引用计数器,每当有一个地方引用它时,引用计数器就会加 1,当引用失效时则会减一;若引用计数器为 0,则说明该对象可以被回收。

优缺点

  • 优点: 实现简单,效率高。
  • 缺点: 无法解决对象之间相互循环引用的问题(即使两个对象已经可以回收但引用计数器依然不为0)。

可达性分析法

原理:
通过已有的一个根对象(GC Roots)开始搜索,如果GC Roots 和 某个对象之间没有任何引用链(搜索不到)则说明该对象可以被回收。

可以作为 GC Roots 的对象:

  1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中JNI(一般说的Native方法)的引用的对象。

注: 一次可达分析后,对象不是立即销毁;而是被标记,在标记后进行一次筛选判断是否需要执行 finalize 方法,如果不需要就会再次进行可达分析,若被标记两次才会真正被回收。


引用详解

引用定义
JDK 1.2 之前,如果 reference 类型的数据存储的值代表另一块内存的起始地址,则称这块内存代表一个引用。
JDK 1.2 之后,引用被分为四种:强引用、软引用、弱引用、虚引用。

四种引用

  1. 强引用: 普遍使用的引用,存在强引用则垃圾回收器绝不会回收它。当内存不足会抛出异常。
  2. 软引用: 当内存空间充足时,垃圾回收器不会回收它;而当内存空间不足时,垃圾回收器就会回收它。可以加速 JVM 的垃圾回收速度。
  3. 弱引用: 相比软引用的生命周期更短,只要扫描到就回收(垃圾回收线程优先级低,所以不会立即回收)。
  4. 虚引用: 相当于没有引用一样,但是可以用来跟踪对象被垃圾回收的活动。

引用队列:
引用队列可以配合软引用、弱引用及虚引用使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中。
作用:程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。


垃圾回收算法

标记-清除算法

JVM 垃圾回收算法_第1张图片
分为两个阶段:

  1. 扫描标记所有需要回收的对象
  2. 清楚所有被标记的对象

优缺点:

  • 优点: 实现简单效率高。
  • 缺点: 会带来大量不连续的内存空间碎片。

复制算法

JVM 垃圾回收算法_第2张图片
将内存分成两个大小完成相同的两块,当执行清理时,将存活对象复制到保留内存的快中,在清空当前的这一块。

优缺点:

  • 优点: 比标记-清除算法效率高。
  • 缺点: 可用内存空间变为了原来的一半。

标记-压缩算法

JVM 垃圾回收算法_第3张图片
标记-压缩,又称为标记-整理算法,标记过程和标记-清除算法一样,在清除前将存活对象全部移到一端,然后清除掉边界外的内存。

优缺点:

  • 优点: 解决了标记-清除、复制算法两种方法的主要问题。
  • 缺点: 效率不高。

分代收集算法

分代收集算法其实是根据各个年代的特点,分别选用上述三种不同的垃圾收集算法。

新生代: 经常有大量对象死亡,可以选择复制算法。
老年代: 对象存活几率较高,可以选择标记-清除或标记-压缩算法。


垃圾收集器

详见:垃圾收集器

  • Serial收集器:
    新生代采用复制算法,老年代采用标记-压缩算法。
    运用单条线程进行回收,且回收时必须暂停其它所有的工作线程。历史最悠久的串行收集器
  • ParNew收集器:
    新生代采用复制算法,老年代采用标记-压缩算法。
    Serial收集器的多线程版本,使用多条线程。属于并行收集器
  • CMS收集器:
    采用标记-清除算法,支持并发收集
    分为以下几个过程:
    1.初始标记: 暂停其它线程,并通过算法对对象进行标记。
    2.并发标记: 同时开启 GC 和其它线程,判断被标记对象是否仍在使用。
    3.重新标记: 修正并发标记期间变动的对象的标记,会暂停其它线程。
    4.并发清除: 开启其它线程,同时 GC 线程开始对标记区域进行清理。
    初始标记、重新标记虽然依然需要暂停其它线程但所用时间很短。
  • G1收集器: 面向服务器端的垃圾收集器。

你可能感兴趣的:(JVM)