JVM之垃圾收集器

在java中,大部分对象都是朝生夕灭的,只有少部分对象能长期存活,所以对堆中的内存进行分区,即新生代和老年代,在不同的代采用不同的收集算法,从而提高内存的利用率。

垃圾收集器

各个垃圾收集器的联系(有连线说明可以配合使用)

1.串行收集器

串行收集器是一个单线程收集器,它进行垃圾回收的时候,必须暂停其他所有的工作线程,即Stop The World,直到它收集完成。它适合Client模式的应用,在单CPU环境下,它简单高效,由于没有线程交互的开销,专心垃圾收集自然可以获得最高的单线程效率。

新生代使用Serial,采用标记-复制算法,老年代使用Serial Old(又叫PS MarkSweep)收集器,采用标记-整理算法,启用参数 -XX:+UseSerialGC

2.并行收集器

 1).ParNew收集器

ParNew 收集器是多线程新生代收集器,采用复制算法,在单CPU下,它的性能并不比Serial收集器好,因为线程交互存在开销,所以适合多CPU系统,由于CMS收集器(老年代收集器)无法和Parallel Scavenge收集器配合工作,所以比较好的选择是ParNew和CMS收集器组合。

使用参数 -XX:+UseParNewGC,   ParNew将会和Serial Old收集器组合进行内存回收

使用参数 -XX:+UseConcMarkSweepGC ,   使用ParNew+CMS+Serial Old进行收集,在CMS收集老年代失败后,使用Serial Old进行收集。

2).Parallel Scavenge收集器

Parallel Scavenge收集器是新生代收集器,采用复制算法,它的特点是关注吞吐量,吞吐量优先,吞吐量=代码运行时间/(代码运行时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉了1分钟,那么吞吐量就是99%,高吞吐率就是高效率利用cpu时间,尽快完成程序的运算任务,主要适合在后台运行而不太需要交互的任务。

使用参数 -XX:+UseParallelGC    使用Parallel Scavenge + Serial Old(PS MarkSweep)收集器收集垃圾

JDK1.8默认使用这个,在控制台中使用java -XX:+PrintCommandLineFlags -version 查看

3).Parallel Old收集器

老年代收集器,在JDK1.6中开始出现,使用标记-整理算法,在注重吞吐量以及CPU资源敏感的场合,可以优先考虑使用Parallel Scavege+Parallel Olds收集器。工作过程如下图:

使用参数 -XX:UseParallelOldGC     使用Parallel Scavege+Parallel Olds进行垃圾回收。

3.CMS收集器

CMS(Concurrent Mark Sweep)收集器采用标记-清除算法,老年代收集器,是一种以获取最短回收停顿时间为目标的收集器,重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。

采用标记-清除算法,但运作过程比前面的要复杂,这个过程分为4个步骤:

    1. 初始标记:会“Stop The World”,但仅仅标记一下GC Roots能直接关联到的对象,速度很快。
    2. 并发标记:GC Roots Tracing,可以和用户线程并发执行。
    3. 重新标记:并发标记期间产生的变动对象存活的再次判断,修正对这些对象的标记,执行时间相对并发标记短,会“Stop The World”。
    4. 并发清除:清除对象,可以和用户线程并发执行。

在垃圾收集过程中,耗时最长的并发标记和并发清除过程过程都可以和用户线程一起工作,所以总体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

CMS的优点是并发收集,低停顿,但也有3个明显的缺点:

  • 对CPU资源非常敏感,在并发收集时,会占用一部分CPU资源使用户线程变慢,总的吞吐量变低,尤其是对低CPU的的影响较大。
  • 无法处理浮动垃圾,由于CMS在并发清理的时候用户线程还在运行,就会有新的垃圾,CMS无法在本次处理掉它们,因为无法处理它们,而且CMS是在老年代快满的时候进行收集,如果还没有收集完,但预留的内存空间不够了,就会发生"Concurrent Mode Failure",这时候启动备案,换用Serial Old 收集器重新收集,这样停顿时间就长了,可以通过减小CMS启动时的内存来减少这种情况发生,在JDK1.6默认时92%开启回收,参数 -XX:CMSInitiatingOccupancyFraction  设置数值,默认-1,具体再写参数的时候再整理。
  • 采用标记-清除算法收集,会产生大量空间碎片。相关参数,
      • -XX:+UseCMSCompactAtFullCollection(默认开启), 用于CMS收集器再顶不住要进行FullGC时开启内存碎片的合并整理过程。
      • -XX:CMSFullGCsBeforeCompaction, 设置执行多少次不压缩的Full GC后,跟着进行一次带压缩的整理(默认为0,即每次Full GC都会进行整理)

 

4.G1(Garbage-First)收集器

优点:

  • 并行与并发,能利用多CPU的优势缩短Stop the World 的停顿时间。
  • 分代收集
  • 空间整合,不会产生内存碎片
  • 可预测的的停顿,G1除了追求低停顿,还能建立可预测的停顿时间模型,能让使用这明确指定在一个长度为M毫秒的时间内,消耗在垃圾收集上的时间不得超过N毫秒

G1收集器收集范围不再是单一的新生代或者老年代,对于G1来说,Java堆的布局就与其它收集器有很大区别,他将整个Java堆划分成多个大小相等的独立区域(region)。G1收集器能建立可预测的停顿时间模型,是因为它可以有计划地避免ui再整个Java堆中进行全区域的垃圾收集,G1,跟踪各个Region里面的垃圾堆的价值大小(回收所获得的空间大小以及回收所需时间的经验值), 再后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的的Region。

G1收集器运作过程:

    1. 初始标记:标记GC Roots能直接关联到的对象,时间很短
    2. 并发标记:从GC Root开始对堆中的对象进行可达性分析,找出存活的对象,可以与用户线程并发执行
    3. 最终标记:需要停顿用户线程,但可以并发进行,在并发标记的时候,用户线程继续运行而使标记产生变动,虚拟机将这些对象变化记录在线程Remembered Set Logs中,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中。
    4. 筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。

 

参考《深入理解Java虚拟机》