垃圾收集器主要用于堆内存中。

JVM垃圾收集器

图中垃圾收集器之间有连线的,就代表可以相互配合使用。

Serial、ParNew、Parallel Scavenge用于新生代;CMS、Serial Old、Paralled Old用于老年代。 并且他们相互之间以相对固定的组合使用。G1是一个独立的收集器不依赖其他6种收集器。


Serial收集器

  • 它是单线程的收集器,使用复制算法。在进行垃圾回收时需要停止其他的所有工作线程(Stop The World)。

ParNew收集器

  • 它其实就是Serial收集器的多线程版本,使用的是复制算法

  • 它在单线程环境下没有Serial好,因为存在线程开销。但随着CPU的增加就可以体现出优势。

  • 它默认开启的收集线程数与CPU的数量相同。

Parallel Scavenge收集器

  • 用于新生代,多线程并行,使用复制算法与ParNew相似

  • 与ParNew不同的是,它更关注垃圾回收的吞吐量。它提供两个参数,一个用于控制最大垃圾收集停顿时间,一个用于控制吞吐量大小

吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

最大垃圾收集停顿时间越小,系统设置新生代越少,GC频率增加。停顿时间缩短是以牺牲吞吐量和新生代空间换取的


Parallel Old收集器

  • Parallel Old收集器,老年代的收集器,是Parallel Scavenge老年代的版本。但是使用的是标记-整理算法

  • Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较不错的应用组合,在注重吞吐量及CPU资源敏感的场合,可以优先考虑Parallel Scavenge收集器+Parallel Old收集器。

Serial Old收集器

  • Serial Old收集器,老年代的收集器,与Serial一样是单线程。不同的是算法用的是标记-整理,因为老年代里面对象的存活率高,如果依旧是用复制算法,需要复制的内容较多,性能较差。

CMS收集器

  • 它是一种以最短垃圾回收停顿时间为目标的收集器,使用的是标记——清除算法。
  • 运作过程分为四步:初始标记->并发标记->重新标记->并发清理。其中初始标记和重新标记这两步仍然需要暂停用户所有线程。

    • 初始标记:仅仅只标记一下GCRoots可以直接关联到的对象,速度很快。

    • 并发标记:是从GCRoot开始继续向下进行标记。

    • 重新标记:是为了修正并发标记期间发生变动的标记,这个阶段停顿时间比初始标记长,比并发标记短。

    • 并发清除:清除老年代的垃圾。

缺点:

  1. 对CPU资源非常敏感:在并发标记阶段虽然不会导致用户线程停顿,但是会占用一部分CPU资源(线程),导致应用程序变慢,总吞吐量降低。

  2. 不能处理浮动垃圾:并发清除阶段,用户线程还在运行,会有新垃圾产生,这部分垃圾只能在下一次GC时清理掉。

  3. 会产生大量空间碎片:由于CMS采用的是标记清除算法,所以会产生大量不连续的空间碎片。


G1收集器

  • 唯一一个可以用于新生代和老年代的垃圾收集器,使用的是标记——整理算法

  • 该收集器会将堆的内存布局分为不同的大小相等Region,G1会追踪每个Region的垃圾堆价值大小,然后在后台维护一个优先列表,优先回收价值最大的Region。

  • 运作过程分为四步:初始标记->并发标记->最终标记->筛选回收。其中初始标记与并发标记的过程和CMS相似。

    • 最终标记:把在并发标记阶段因程序运行而导致发生变动的那一部分标记记录下来,写入到线程的Remembered Set Logs里,同时与Remembered Set合并。

    • 筛选回收:首先对各个Region价值和回收成本进行排序,然后筛选得到一个最好的方案,并进行回收。