2018/08/27 Garbage Collection Basics

垃圾对象判断

垃圾回收第一步就是确定哪些对象需要被回收,方法有 2 种:

  1. 引用计数
    引用计数就是在全局维护一个 Map 保存对象的引用,当引用一个对象的时候,就给对应的 value 加 1 ,当这个引用失效的时候,就将对应的 value 减 1 。当 value 的值为 0 的时候,就说明此刻该对象是垃圾对象,可以被回收掉。引用计数的缺点:
    1) 无法解决循环引用的问题。
    2) 引用计数需要实时更新,开销大。
  2. 可达性分析
    可达性分析就是从 GC root 开始遍历,能遍历到的对象就不会被回收。hotspot 的定义的 GC roots 有:
    1) 虚拟机栈中引用的对象(栈帧中局部变量表中的对象)。
    2) 方法区中静态属性和常量引用的对象。
    3) 本地方法栈中的对象。

垃圾收集算法

  1. 标记-清除算法
    标记清除算法是最基本的垃圾收集算法。算法分为标记和清除两个阶段。首先,标记出所有需要回收的对象,在标记完后统一回收所有被标记的对象。标记清楚算法缺点:
    1)效率不高;
    2)清除后会产生大量不连续的内存碎片,碎片太多,会导致在创建大对象时没有足够的连续内存,会再次触发垃圾回收。
  2. 复制算法
    复制算法是为了解决标记清除算法的效率问题提出的。复制算法将内存分为大小相等的两份,每次使用其中一份。当其中一块用完了,就将对象复制到另一块上,然后再把原来的内存一次清除。这样每次都对一整块内存进行操作,不会出现内存碎片的问题,简单高效,但是缺点也很明显,就是每次只有一半的内存可以使用。复制算法在新生代的垃圾回收中被大量采用,新生代被分为 Eden 和 Survivor 。当进行垃圾回收时, Eden 和其中一个 Survivor 的对象会被复制到另一个 Survivor 中。如果 Survivor 空间不够,对象会进入 Old 区,这被称为分配担保(Handle Promotion)。
  3. 标记-整理算法
    由于复制算法的特点,一般不会在 Old 区使用。根据 Old 区的特点,提出了另一种标记-整理算法。首先对对象进行标记,然后将对象向一端移动,然后清理掉边界以外的内存空间。
  4. 分代收集算法
    分代收技算法是根据对象的存活时间,将内存分为几块,根据不同区域的特点选择不同的垃圾回收算法。如新生代中对象创建和死亡的频率很高,所以大多采用复制算法。老年代没有额外的内存进行分配担保,所以大多使用标记-删除和标记-复制算法。

垃圾收集器

垃圾收集器是对垃圾收集算法的实现,垃圾收集器可以搭配使用。

  • 年轻代垃圾收集器
    1. Serial 收集器
    Serial 收集器是单线程的,它会在进行垃圾回收时,会暂停其他所有线程(stop-the-world)。Serial 收集器简单高效,虽然会造成停顿,但是在 Client 模式下,Serial 收集器是一个不错的选择。
    2. ParNew 收集器
    ParNew 是 Serial 的多线程版本,由于 CMS 只能搭配 ParNew 使用,所以 ParNew 是 Server 模式下的新生代收集器首选。
    3. Parallel Scavenge 收集器
    Parallel Scavenge 是新生代收集器,使用复制算法,并行的多线程收集器。Parallel Scavenge 的衡量标准是吞吐量。吞吐量是 CPU 用于运行用户代码与 CPU 总消耗时间的比值。
  • 老年代垃圾收集器
    1. Serial Old 收集器
    Serial Old 是 Serial 的老年代版本。会暂停所有线程,使用标记整理算法。
    2. CMS 收集器
    Concurrent Mark Sweep 目标是减少停顿。使用标记清除算法。
    3. Parallel Old 收集器
    Parallel Old 是 Parallel Scavenge 的老年代版本,搭配 Parallel Scanvenge 使用。

  • G1 收集器

串行,并行和并发

串行:应用和 GC 是顺序执行的,在 GC 的时候应用需要停止,只有一个线程在 GC 。
并行:应用和 GC 是顺序执行的,在 GC 的时候应用需要停止,有多个线程在 GC 。
并发:应用和 GC 是同时执行的,可能同时执行,也可能交替执行。